github.com/dolthub/go-mysql-server@v0.18.0/sql/expression/function/aggregation/json_agg.go (about) 1 // Copyright 2021 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 aggregation 16 17 import ( 18 "fmt" 19 20 "github.com/dolthub/go-mysql-server/sql" 21 "github.com/dolthub/go-mysql-server/sql/types" 22 ) 23 24 // JSON_OBJECTAGG(key, value) [over_clause] 25 // 26 // JSONObjectAgg Takes two column names or expressions as arguments, the first of these being used as a key and the 27 // second as a value, and returns a JSON object containing key-value pairs. Returns NULL if the result contains no rows, 28 // or in the event of an error. An error occurs if any key name is NULL or the number of arguments is not equal to 2. 29 // 30 // https://dev.mysql.com/doc/refman/8.0/en/aggregate-functions.html#function_json-objectagg 31 // 32 // see also: https://dev.mysql.com/doc/refman/8.0/en/json.html#json-normalization 33 type JSONObjectAgg struct { 34 key sql.Expression 35 value sql.Expression 36 window *sql.WindowDefinition 37 id sql.ColumnId 38 } 39 40 var _ sql.FunctionExpression = (*JSONObjectAgg)(nil) 41 var _ sql.Aggregation = (*JSONObjectAgg)(nil) 42 var _ sql.WindowAdaptableExpression = (*JSONObjectAgg)(nil) 43 var _ sql.CollationCoercible = (*JSONObjectAgg)(nil) 44 45 // NewJSONObjectAgg creates a new JSONObjectAgg function. 46 func NewJSONObjectAgg(key, value sql.Expression) sql.Expression { 47 return &JSONObjectAgg{key: key, value: value} 48 } 49 50 // Id implements the Aggregation interface 51 func (j *JSONObjectAgg) Id() sql.ColumnId { 52 return j.id 53 } 54 55 // WithId implements the Aggregation interface 56 func (j *JSONObjectAgg) WithId(id sql.ColumnId) sql.IdExpression { 57 ret := *j 58 ret.id = id 59 return &ret 60 } 61 62 // FunctionName implements sql.FunctionExpression 63 func (j *JSONObjectAgg) FunctionName() string { 64 return "json_objectagg" 65 } 66 67 // Description implements sql.FunctionExpression 68 func (j *JSONObjectAgg) Description() string { 69 return "returns result set as a single JSON object." 70 } 71 72 // Resolved implements the Expression interface. 73 func (j *JSONObjectAgg) Resolved() bool { 74 return j.key.Resolved() && j.value.Resolved() 75 } 76 77 func (j *JSONObjectAgg) String() string { 78 return fmt.Sprintf("JSON_OBJECTAGG(%s, %s)", j.key, j.value) 79 } 80 81 // Type implements the Expression interface. 82 func (j *JSONObjectAgg) Type() sql.Type { 83 return types.JSON 84 } 85 86 // CollationCoercibility implements the interface sql.CollationCoercible. 87 func (*JSONObjectAgg) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) { 88 return ctx.GetCharacterSet().BinaryCollation(), 2 89 } 90 91 // IsNullable implements the Expression interface. 92 func (j *JSONObjectAgg) IsNullable() bool { 93 return false 94 } 95 96 // Children implements the Expression interface. 97 func (j *JSONObjectAgg) Children() []sql.Expression { 98 return []sql.Expression{j.key, j.value} 99 } 100 101 // WithChildren implements the Expression interface. 102 func (j *JSONObjectAgg) WithChildren(children ...sql.Expression) (sql.Expression, error) { 103 if len(children) != 2 { 104 return nil, sql.ErrInvalidChildrenNumber.New(j, len(children), 2) 105 } 106 107 return NewJSONObjectAgg(children[0], children[1]), nil 108 } 109 110 // WithWindow implements sql.Aggregation 111 func (j *JSONObjectAgg) WithWindow(window *sql.WindowDefinition) sql.WindowAdaptableExpression { 112 nj := *j 113 nj.window = window 114 return &nj 115 } 116 117 // Window implements sql.Aggregation 118 func (j *JSONObjectAgg) Window() *sql.WindowDefinition { 119 return j.window 120 } 121 122 // NewBuffer implements the Aggregation interface. 123 func (j *JSONObjectAgg) NewBuffer() (sql.AggregationBuffer, error) { 124 row := make(map[string]interface{}) 125 return &jsonObjectBuffer{row, j}, nil 126 } 127 128 // NewWindowFunctionAggregation implements sql.WindowAdaptableExpression 129 func (j *JSONObjectAgg) NewWindowFunction() (sql.WindowFunction, error) { 130 return NewWindowedJSONObjectAgg(j), nil 131 } 132 133 // Eval implements the Expression interface. 134 func (j *JSONObjectAgg) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { 135 return nil, ErrEvalUnsupportedOnAggregation.New("JSONObjectAgg") 136 } 137 138 type jsonObjectBuffer struct { 139 vals map[string]interface{} 140 joa *JSONObjectAgg 141 } 142 143 // Update implements the AggregationBuffer interface. 144 func (j *jsonObjectBuffer) Update(ctx *sql.Context, row sql.Row) error { 145 key, err := j.joa.key.Eval(ctx, row) 146 if err != nil { 147 return err 148 } 149 150 // An error occurs if any key name is NULL 151 if key == nil { 152 return sql.ErrJSONObjectAggNullKey.New() 153 } 154 155 val, err := j.joa.value.Eval(ctx, row) 156 if err != nil { 157 return err 158 } 159 160 // unwrap JSON values 161 if js, ok := val.(sql.JSONWrapper); ok { 162 val = js.ToInterface() 163 } 164 165 // Update the map. 166 keyAsString, _, err := types.LongText.Convert(key) 167 if err != nil { 168 return nil 169 } 170 j.vals[keyAsString.(string)] = val 171 172 return nil 173 } 174 175 // Eval implements the AggregationBuffer interface. 176 func (j *jsonObjectBuffer) Eval(ctx *sql.Context) (interface{}, error) { 177 // When no rows are present return NULL 178 if len(j.vals) == 0 { 179 return nil, nil 180 } 181 182 return types.JSONDocument{Val: j.vals}, nil 183 } 184 185 // Dispose implements the Disposable interface. 186 func (j *jsonObjectBuffer) Dispose() { 187 }