diff --git a/pkg/sql/logictest/testdata/logic_test/information_schema b/pkg/sql/logictest/testdata/logic_test/information_schema index 829f32690fc4..764afd00e561 100644 --- a/pkg/sql/logictest/testdata/logic_test/information_schema +++ b/pkg/sql/logictest/testdata/logic_test/information_schema @@ -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 diff --git a/pkg/sql/parser/sql.y b/pkg/sql/parser/sql.y index 7578526c43bc..34dd85a1116c 100644 --- a/pkg/sql/parser/sql.y +++ b/pkg/sql/parser/sql.y @@ -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: @@ -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: diff --git a/pkg/sql/sem/tree/col_types_test.go b/pkg/sql/sem/tree/col_types_test.go index fe7b3fa627f0..c93df3178eca 100644 --- a/pkg/sql/sem/tree/col_types_test.go +++ b/pkg/sql/sem/tree/col_types_test.go @@ -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")}, diff --git a/pkg/sql/table_test.go b/pkg/sql/table_test.go index 04d0ecaeb1b8..18939cc86ed5 100644 --- a/pkg/sql/table_test.go +++ b/pkg/sql/table_test.go @@ -160,7 +160,7 @@ func TestMakeTableDescColumns(t *testing.T) { }, { `"char"`, - types.MakeQChar(0), + types.QChar, true, }, { diff --git a/pkg/sql/types/oid.go b/pkg/sql/types/oid.go index ffc2442e91e1..2e567e4c42fe 100644 --- a/pkg/sql/types/oid.go +++ b/pkg/sql/types/oid.go @@ -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, diff --git a/pkg/sql/types/types.go b/pkg/sql/types/types.go index 7656f0051903..33369a735cce 100644 --- a/pkg/sql/types/types.go +++ b/pkg/sql/types/types.go @@ -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. @@ -583,7 +592,7 @@ 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. @@ -591,16 +600,6 @@ var ( // 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 ( @@ -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: @@ -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 diff --git a/pkg/sql/types/types_test.go b/pkg/sql/types/types_test.go index 2fd5a284e42c..37dc54fe70ed 100644 --- a/pkg/sql/types/types_test.go +++ b/pkg/sql/types/types_test.go @@ -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}}}, @@ -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}}}, @@ -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}}, } @@ -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 { diff --git a/pkg/workload/schemachange/type_resolver.go b/pkg/workload/schemachange/type_resolver.go index cc08b2202377..8e8be208c35e 100644 --- a/pkg/workload/schemachange/type_resolver.go +++ b/pkg/workload/schemachange/type_resolver.go @@ -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