Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 30 additions & 22 deletions server/functions/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,30 +106,38 @@ var json_build_object = framework.Function1{
Return: pgtypes.Json,
Parameters: [1]*pgtypes.DoltgresType{pgtypes.AnyArray},
Variadic: true,
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val1 any) (any, error) {
inputArray := val1.([]any)
if len(inputArray)%2 != 0 {
return nil, sql.ErrInvalidArgumentNumber.New("json_build_object", "even number of arguments", len(inputArray))
Callable: func(ctx *sql.Context, argTypes [2]*pgtypes.DoltgresType, val1 any) (any, error) {
json, err := buildJsonObject("json_build_object", argTypes, val1)
if err != nil {
return nil, err
}
jsonObject := make(map[string]any)
var key string
for i, e := range inputArray {
if i%2 == 0 {
var ok bool
key, ok = e.(string)
if !ok {
// TODO: This isn't correct for every type we might use as a value. To get better type info to transform
// every value into its string format, we need to pass detailed arg type info for the vararg params (the
// unused param in the function call).
key = fmt.Sprintf("%v", e)
}
} else {
jsonObject[key] = e
key = ""
return string(json), nil
},
}

// buildJsonObject constructs a json object from the input array provided, which are alternating keys and values.
func buildJsonObject(fnName string, _ [2]*pgtypes.DoltgresType, val1 any) ([]byte, error) {
inputArray := val1.([]any)
if len(inputArray)%2 != 0 {
return nil, sql.ErrInvalidArgumentNumber.New(fnName, "even number of arguments", len(inputArray))
}
jsonObject := make(map[string]any)
var key string
for i, e := range inputArray {
if i%2 == 0 {
var ok bool
key, ok = e.(string)
if !ok {
// TODO: This isn't correct for every type we might use as a value. To get better type info to transform
// every value into its string format, we need to pass detailed arg type info for the vararg params (the
// unused param in the function call).
key = fmt.Sprintf("%v", e)
}
} else {
jsonObject[key] = e
key = ""
}
}

json, err := json.Marshal(jsonObject)
return string(json), err
},
return json.Marshal(jsonObject)
}
46 changes: 46 additions & 0 deletions server/functions/jsonb.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ func initJsonB() {
framework.RegisterFunction(jsonb_recv)
framework.RegisterFunction(jsonb_send)
framework.RegisterFunction(jsonb_cmp)
framework.RegisterFunction(jsonb_build_array)
framework.RegisterFunction(jsonb_build_object)

}

// jsonb_in represents the PostgreSQL function of jsonb type IO input.
Expand Down Expand Up @@ -108,3 +111,46 @@ var jsonb_cmp = framework.Function2{
return int32(pgtypes.JsonValueCompare(ab.Value, bb.Value)), nil
},
}

// jsonb_build_array represents the PostgreSQL function jsonb_build_array.
var jsonb_build_array = framework.Function1{
Name: "jsonb_build_array",
Return: pgtypes.JsonB,
Parameters: [1]*pgtypes.DoltgresType{pgtypes.AnyArray},
Variadic: true,
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val1 any) (any, error) {
inputArray := val1.([]any)
json, err := json.Marshal(inputArray)
if err != nil {
return nil, err
}

jsonDoc, err := pgtypes.UnmarshalToJsonDocument(json)
if err != nil {
return nil, err
}

return jsonDoc, nil
},
}

// jsonb_build_object represents the PostgreSQL function jsonb_build_object.
var jsonb_build_object = framework.Function1{
Name: "jsonb_build_object",
Return: pgtypes.JsonB,
Parameters: [1]*pgtypes.DoltgresType{pgtypes.AnyArray},
Variadic: true,
Callable: func(ctx *sql.Context, argTypes [2]*pgtypes.DoltgresType, val1 any) (any, error) {
json, err := buildJsonObject("jsonb_build_object", argTypes, val1)
if err != nil {
return nil, err
}

jsonDoc, err := pgtypes.UnmarshalToJsonDocument(json)
if err != nil {
return nil, err
}

return jsonDoc, nil
},
}
42 changes: 41 additions & 1 deletion testing/go/functions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -917,7 +917,7 @@ func TestSystemInformationFunctions(t *testing.T) {
// TODO: ALTER SEQUENCE OWNED BY is not supported yet. When the sequence is created
// explicitly, separate from the column, the owner must be udpated before
// pg_get_serial_sequence() will identify it.
//`ALTER SEQUENCE t2_id_seq OWNED BY t2.id;`,
// `ALTER SEQUENCE t2_id_seq OWNED BY t2.id;`,
},
Assertions: []ScriptTestAssertion{
{
Expand Down Expand Up @@ -1004,6 +1004,46 @@ func TestJsonFunctions(t *testing.T) {
},
},
},
{
Name: "jsonb_build_array",
Assertions: []ScriptTestAssertion{
{
Query: `SELECT jsonb_build_array(1, 2, 3);`,
Cols: []string{"jsonb_build_array"},
Expected: []sql.Row{{`[1, 2, 3]`}},
},
{
Query: `SELECT jsonb_build_array(1, '2', 3);`,
Cols: []string{"jsonb_build_array"},
Expected: []sql.Row{{`[1, "2", 3]`}},
},
{
Query: `SELECT jsonb_build_array();`,
Skip: true, // variadic functions can't handle 0 arguments right now
Cols: []string{"jsonb_build_array"},
Expected: []sql.Row{{`[]`}},
},
},
},
{
Name: "jsonb_build_object",
Assertions: []ScriptTestAssertion{
{
Query: `SELECT jsonb_build_object('a', 2, 'b', 4);`,
Cols: []string{"jsonb_build_object"},
Expected: []sql.Row{{`{"a": 2, "b": 4}`}},
},
{
Query: `SELECT jsonb_build_object('a', 2, 'b');`,
ExpectedErr: "even number",
},
{
Query: `SELECT jsonb_build_object(1, 2, 'b', 3);`,
Cols: []string{"jsonb_build_object"},
Expected: []sql.Row{{`{"1": 2, "b": 3}`}},
},
},
},
})
}

Expand Down
Loading