github.com/dolthub/go-mysql-server@v0.18.0/sql/coercibility.go (about) 1 // Copyright 2023 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package sql 16 17 import "strings" 18 19 // CollationCoercible represents the coercibility of an expression or node. Although the resulting value from the node 20 // or expression may be NULL, this interface returns the coercibility as though a NULL would not be returned. 21 type CollationCoercible interface { 22 // CollationCoercibility returns the collation and coercibility of the expression or node. 23 CollationCoercibility(ctx *Context) (collation CollationID, coercibility byte) 24 } 25 26 // ResolveCoercibility returns the collation to use by comparing coercibility, along with giving priority to binary 27 // collations. This is an approximation of MySQL's coercibility rules: 28 // https://dev.mysql.com/doc/refman/8.0/en/charset-collation-coercibility.html 29 // As we do not implement the full coercion pipeline, we make some assumptions when information is missing, and never 30 // error even when MySQL would find an expression invalid. 31 func ResolveCoercibility(leftCollation CollationID, leftCoercibility byte, rightCollation CollationID, rightCoercibility byte) (CollationID, byte) { 32 if leftCoercibility < rightCoercibility { 33 return leftCollation, leftCoercibility 34 } else if leftCoercibility > rightCoercibility { 35 return rightCollation, rightCoercibility 36 } else if leftCollation == rightCollation { 37 return leftCollation, leftCoercibility 38 } else if leftCollation == Collation_Unspecified { 39 return rightCollation, rightCoercibility 40 } else if rightCollation == Collation_Unspecified { 41 return leftCollation, leftCoercibility 42 } else { // Collations are not equal 43 leftCharset := leftCollation.CharacterSet() 44 rightCharset := rightCollation.CharacterSet() 45 if leftCharset != rightCharset { 46 if leftCharset.MaxLength() == 1 && rightCharset.MaxLength() > 1 { // Left non-Unicode, Right Unicode 47 return rightCollation, rightCoercibility 48 } else if leftCharset.MaxLength() > 1 && rightCharset.MaxLength() == 1 { // Left Unicode, Right non-Unicode 49 return leftCollation, leftCoercibility 50 } else { 51 return Collation_binary, 7 52 } 53 } else { // Character sets are equal 54 // If the right collation is not _bin, then we default to the left collation (regardless of whether it is 55 // or is not _bin). 56 if strings.HasSuffix(rightCollation.Name(), "_bin") { 57 return rightCollation, rightCoercibility 58 } else { 59 return leftCollation, leftCoercibility 60 } 61 } 62 } 63 } 64 65 // GetCoercibility returns the coercibility of the given node or expression. 66 func GetCoercibility(ctx *Context, nodeOrExpr interface{}) (collation CollationID, coercibility byte) { 67 if nodeOrExpr == nil { 68 return Collation_binary, 6 69 } 70 if cc, ok := nodeOrExpr.(CollationCoercible); ok { 71 return cc.CollationCoercibility(ctx) 72 } 73 collation = Collation_binary 74 coercibility = 7 75 // We check for Node, Expressioner, and Expression and take the lowest coercibility since CollationCoercible was 76 // not explicitly implemented 77 if n, ok := nodeOrExpr.(Node); ok { 78 for _, child := range n.Children() { 79 nextCollation, nextCoercibility := GetCoercibility(ctx, child) 80 collation, coercibility = ResolveCoercibility(collation, coercibility, nextCollation, nextCoercibility) 81 } 82 } 83 if e, ok := nodeOrExpr.(Expressioner); ok { 84 for _, child := range e.Expressions() { 85 nextCollation, nextCoercibility := GetCoercibility(ctx, child) 86 collation, coercibility = ResolveCoercibility(collation, coercibility, nextCollation, nextCoercibility) 87 } 88 } 89 if e, ok := nodeOrExpr.(Expression); ok { 90 for _, child := range e.Children() { 91 nextCollation, nextCoercibility := GetCoercibility(ctx, child) 92 collation, coercibility = ResolveCoercibility(collation, coercibility, nextCollation, nextCoercibility) 93 } 94 } 95 return collation, coercibility 96 }