@@ -288,15 +288,34 @@ func typesNeedCharset(tp byte) bool {
288
288
return false
289
289
}
290
290
291
- func setCharsetCollationFlenDecimal (tp * types.FieldType , tblCharset string , dbCharset string ) error {
291
+ func setCharsetCollationFlenDecimal (tp * types.FieldType , specifiedCollates [] string , tblCharset string , dbCharset string ) error {
292
292
tp .Charset = strings .ToLower (tp .Charset )
293
293
tp .Collate = strings .ToLower (tp .Collate )
294
294
if len (tp .Charset ) == 0 {
295
295
if typesNeedCharset (tp .Tp ) {
296
- var err error
297
- tp .Charset , tp .Collate , err = ResolveCharsetCollation (tblCharset , dbCharset )
298
- if err != nil {
299
- return errors .Trace (err )
296
+ if len (specifiedCollates ) == 0 {
297
+ // Both the charset and collate are not specified.
298
+ var err error
299
+ tp .Charset , tp .Collate , err = ResolveCharsetCollation (tblCharset , dbCharset )
300
+ if err != nil {
301
+ return errors .Trace (err )
302
+ }
303
+ } else {
304
+ // The charset is not specified but the collate is.
305
+ // We should derive charset from it's collate specified rather than getting from table and db.
306
+ // It is handled like mysql's logic, use derived charset to judge conflict with next collate.
307
+ for _ , spc := range specifiedCollates {
308
+ derivedCollation , err := charset .GetCollationByName (spc )
309
+ if err != nil {
310
+ return errors .Trace (err )
311
+ }
312
+ if len (tp .Charset ) == 0 {
313
+ tp .Charset = derivedCollation .CharsetName
314
+ } else if tp .Charset != derivedCollation .CharsetName {
315
+ return ErrCollationCharsetMismatch .GenWithStackByArgs (derivedCollation .Name , tp .Charset )
316
+ }
317
+ tp .Collate = derivedCollation .Name
318
+ }
300
319
}
301
320
} else {
302
321
tp .Charset = charset .CharsetBin
@@ -307,10 +326,25 @@ func setCharsetCollationFlenDecimal(tp *types.FieldType, tblCharset string, dbCh
307
326
return errUnsupportedCharset .GenWithStackByArgs (tp .Charset , tp .Collate )
308
327
}
309
328
if len (tp .Collate ) == 0 {
310
- var err error
311
- tp .Collate , err = charset .GetDefaultCollation (tp .Charset )
312
- if err != nil {
313
- return errors .Trace (err )
329
+ if len (specifiedCollates ) == 0 {
330
+ // The charset is specified, but the collate is not.
331
+ var err error
332
+ tp .Collate , err = charset .GetDefaultCollation (tp .Charset )
333
+ if err != nil {
334
+ return errors .Trace (err )
335
+ }
336
+ } else {
337
+ // Both the charset and collate are specified.
338
+ for _ , spc := range specifiedCollates {
339
+ derivedCollation , err := charset .GetCollationByName (spc )
340
+ if err != nil {
341
+ return errors .Trace (err )
342
+ }
343
+ if tp .Charset != derivedCollation .CharsetName {
344
+ return ErrCollationCharsetMismatch .GenWithStackByArgs (derivedCollation .Name , tp .Charset )
345
+ }
346
+ tp .Collate = derivedCollation .Name
347
+ }
314
348
}
315
349
}
316
350
}
@@ -333,8 +367,10 @@ func setCharsetCollationFlenDecimal(tp *types.FieldType, tblCharset string, dbCh
333
367
// outPriKeyConstraint is the primary key constraint out of column definition. such as: create table t1 (id int , age int, primary key(id));
334
368
func buildColumnAndConstraint (ctx sessionctx.Context , offset int ,
335
369
colDef * ast.ColumnDef , outPriKeyConstraint * ast.Constraint , tblCharset , dbCharset string ) (* table.Column , []* ast.Constraint , error ) {
336
- err := setCharsetCollationFlenDecimal (colDef .Tp , tblCharset , dbCharset )
337
- if err != nil {
370
+ // specifiedCollates refers to collates in colDef.Options, should handle them together.
371
+ specifiedCollates := extractCollateFromOption (colDef )
372
+
373
+ if err := setCharsetCollationFlenDecimal (colDef .Tp , specifiedCollates , tblCharset , dbCharset ); err != nil {
338
374
return nil , nil , errors .Trace (err )
339
375
}
340
376
col , cts , err := columnDefToCol (ctx , offset , colDef , outPriKeyConstraint )
@@ -2025,7 +2061,11 @@ func (d *ddl) getModifiableColumnJob(ctx sessionctx.Context, ident ast.Ident, or
2025
2061
newCol .FieldType .Charset = col .FieldType .Charset
2026
2062
newCol .FieldType .Collate = col .FieldType .Collate
2027
2063
}
2028
- err = setCharsetCollationFlenDecimal (& newCol .FieldType , t .Meta ().Charset , schema .Charset )
2064
+ // specifiedCollates refers to collates in colDef.Option. When setting charset and collate here we
2065
+ // should take the collate in colDef.Option into consideration rather than handling it separately
2066
+ specifiedCollates := extractCollateFromOption (specNewColumn )
2067
+
2068
+ err = setCharsetCollationFlenDecimal (& newCol .FieldType , specifiedCollates , t .Meta ().Charset , schema .Charset )
2029
2069
if err != nil {
2030
2070
return nil , errors .Trace (err )
2031
2071
}
@@ -2677,3 +2717,19 @@ func buildPartitionInfo(meta *model.TableInfo, d *ddl, spec *ast.AlterTableSpec)
2677
2717
}
2678
2718
return part , nil
2679
2719
}
2720
+
2721
+ // extractCollateFromOption take collates(may multiple) in option into consideration
2722
+ // when handle charset and collate of a column, rather than handling it separately.
2723
+ func extractCollateFromOption (def * ast.ColumnDef ) []string {
2724
+ specifiedCollates := make ([]string , 0 , 0 )
2725
+ for i := 0 ; i < len (def .Options ); i ++ {
2726
+ op := def .Options [i ]
2727
+ if op .Tp == ast .ColumnOptionCollate {
2728
+ specifiedCollates = append (specifiedCollates , op .StrValue )
2729
+ def .Options = append (def .Options [:i ], def .Options [i + 1 :]... )
2730
+ // maintain the correct index
2731
+ i --
2732
+ }
2733
+ }
2734
+ return specifiedCollates
2735
+ }
0 commit comments