github.com/dolthub/go-mysql-server@v0.18.0/sql/types/json.go (about)

     1  // Copyright 2022 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 types
    16  
    17  import (
    18  	"bytes"
    19  	"encoding/json"
    20  	"reflect"
    21  
    22  	"github.com/dolthub/vitess/go/sqltypes"
    23  	"github.com/dolthub/vitess/go/vt/proto/query"
    24  
    25  	"github.com/dolthub/go-mysql-server/sql"
    26  )
    27  
    28  var (
    29  	jsonValueType = reflect.TypeOf((*sql.JSONWrapper)(nil)).Elem()
    30  
    31  	MaxJsonFieldByteLength = int64(1024) * int64(1024) * int64(1024)
    32  )
    33  
    34  var JSON sql.Type = JsonType{}
    35  var _ sql.CollationCoercible = JsonType{}
    36  
    37  type JsonType struct{}
    38  
    39  // Compare implements Type interface.
    40  func (t JsonType) Compare(a interface{}, b interface{}) (int, error) {
    41  	if hasNulls, res := CompareNulls(a, b); hasNulls {
    42  		return res, nil
    43  	}
    44  	return CompareJSON(a, b)
    45  }
    46  
    47  // Convert implements Type interface.
    48  func (t JsonType) Convert(v interface{}) (doc interface{}, inRange sql.ConvertInRange, err error) {
    49  	switch v := v.(type) {
    50  	case sql.JSONWrapper:
    51  		return v, sql.InRange, nil
    52  	case []byte:
    53  		if int64(len(v)) > MaxJsonFieldByteLength {
    54  			return nil, sql.InRange, ErrLengthTooLarge.New(len(v), MaxJsonFieldByteLength)
    55  		}
    56  		err = json.Unmarshal(v, &doc)
    57  		if err != nil {
    58  			return nil, sql.OutOfRange, sql.ErrInvalidJson.New(err.Error())
    59  		}
    60  	case string:
    61  		charsetMaxLength := sql.Collation_Default.CharacterSet().MaxLength()
    62  		length := int64(len(v)) * charsetMaxLength
    63  		if length > MaxJsonFieldByteLength {
    64  			return nil, sql.InRange, ErrLengthTooLarge.New(length, MaxJsonFieldByteLength)
    65  		}
    66  		err = json.Unmarshal([]byte(v), &doc)
    67  		if err != nil {
    68  			return nil, sql.OutOfRange, sql.ErrInvalidJson.New(err.Error())
    69  		}
    70  	default:
    71  		// if |v| can be marshalled, it contains
    72  		// a valid JSON document representation
    73  		if b, berr := json.Marshal(v); berr == nil {
    74  			if int64(len(b)) > MaxJsonFieldByteLength {
    75  				return nil, sql.InRange, ErrLengthTooLarge.New(len(b), MaxJsonFieldByteLength)
    76  			}
    77  			err = json.Unmarshal(b, &doc)
    78  			if err != nil {
    79  				return nil, sql.OutOfRange, sql.ErrInvalidJson.New(err.Error())
    80  			}
    81  		}
    82  	}
    83  	if err != nil {
    84  		return nil, sql.OutOfRange, err
    85  	}
    86  	return JSONDocument{Val: doc}, sql.InRange, nil
    87  }
    88  
    89  // Equals implements the Type interface.
    90  func (t JsonType) Equals(otherType sql.Type) bool {
    91  	_, ok := otherType.(JsonType)
    92  	return ok
    93  }
    94  
    95  // MaxTextResponseByteLength implements the Type interface
    96  func (t JsonType) MaxTextResponseByteLength(_ *sql.Context) uint32 {
    97  	return uint32(MaxJsonFieldByteLength*sql.Collation_Default.CharacterSet().MaxLength()) - 1
    98  }
    99  
   100  // Promote implements the Type interface.
   101  func (t JsonType) Promote() sql.Type {
   102  	return t
   103  }
   104  
   105  // SQL implements Type interface.
   106  func (t JsonType) SQL(ctx *sql.Context, dest []byte, v interface{}) (sqltypes.Value, error) {
   107  	if v == nil {
   108  		return sqltypes.NULL, nil
   109  	}
   110  
   111  	// Convert to jsonType
   112  	jsVal, _, err := t.Convert(v)
   113  	if err != nil {
   114  		return sqltypes.NULL, err
   115  	}
   116  	js := jsVal.(sql.JSONWrapper)
   117  
   118  	var val []byte
   119  	switch j := js.(type) {
   120  	case JSONStringer:
   121  		str, err := j.JSONString()
   122  		if err != nil {
   123  			return sqltypes.NULL, err
   124  		}
   125  		val = AppendAndSliceString(dest, str)
   126  	default:
   127  		jsonBytes, err := json.Marshal(js.ToInterface())
   128  		if err != nil {
   129  			return sqltypes.NULL, err
   130  		}
   131  
   132  		jsonBytes = bytes.ReplaceAll(jsonBytes, []byte(",\""), []byte(", \""))
   133  		jsonBytes = bytes.ReplaceAll(jsonBytes, []byte("\":"), []byte("\": "))
   134  		val = AppendAndSliceBytes(dest, jsonBytes)
   135  	}
   136  
   137  	return sqltypes.MakeTrusted(sqltypes.TypeJSON, val), nil
   138  }
   139  
   140  // String implements Type interface.
   141  func (t JsonType) String() string {
   142  	return "json"
   143  }
   144  
   145  // Type implements Type interface.
   146  func (t JsonType) Type() query.Type {
   147  	return sqltypes.TypeJSON
   148  }
   149  
   150  // ValueType implements Type interface.
   151  func (t JsonType) ValueType() reflect.Type {
   152  	return jsonValueType
   153  }
   154  
   155  // Zero implements Type interface.
   156  func (t JsonType) Zero() interface{} {
   157  	// MySQL throws an error for INSERT IGNORE, UPDATE IGNORE, etc. when bad json is encountered:
   158  	// ERROR 3140 (22032): Invalid JSON text: "Invalid value." at position 0 in value for column 'table.column'.
   159  	return nil
   160  }
   161  
   162  // CollationCoercibility implements sql.CollationCoercible interface.
   163  func (JsonType) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
   164  	return sql.Collation_Default, 5
   165  }
   166  
   167  // DeepCopyJson implements deep copy of JSON document
   168  func DeepCopyJson(v interface{}) interface{} {
   169  	if v == nil {
   170  		return nil
   171  	}
   172  
   173  	switch v.(type) {
   174  	case map[string]interface{}:
   175  		m := v.(map[string]interface{})
   176  		newMap := make(map[string]interface{})
   177  		for k, value := range m {
   178  			newMap[k] = DeepCopyJson(value)
   179  		}
   180  		return newMap
   181  	case []interface{}:
   182  		arr := v.([]interface{})
   183  		newArray := make([]interface{}, len(arr))
   184  		for i, doc := range arr {
   185  			newArray[i] = DeepCopyJson(doc)
   186  		}
   187  		return newArray
   188  	case bool, string, float64, float32,
   189  		int, int8, int16, int32, int64,
   190  		uint, uint8, uint16, uint32, uint64:
   191  		return v
   192  	default:
   193  		return nil
   194  	}
   195  }
   196  
   197  func MustJSON(s string) JSONDocument {
   198  	var doc interface{}
   199  	if err := json.Unmarshal([]byte(s), &doc); err != nil {
   200  		panic(err)
   201  	}
   202  	return JSONDocument{Val: doc}
   203  }