Skip to content

Commit

Permalink
added json predicate detector and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
priyanshi-yb committed Dec 21, 2024
1 parent facaced commit ea061d7
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 15 deletions.
2 changes: 2 additions & 0 deletions yb-voyager/src/query/queryissue/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ const (

AGGREGATE_FUNCTION = "AGGREGATE_FUNCTION"
AGGREGATION_FUNCTIONS_NAME = "Aggregate Functions"
JSON_PREDICATE = "JSON_PREDICATE"
JSON_PREDICATE_NAME = "Json Predicate expression"
JSON_CONSTRUCTOR_FUNCTION = "JSON_CONSTRUCTOR_FUNCTION"
JSON_CONSTRUCTOR_FUNCTION_NAME = "Json Constructor Functions"
JSON_QUERY_FUNCTION = "JSON_QUERY_FUNCTION"
Expand Down
26 changes: 26 additions & 0 deletions yb-voyager/src/query/queryissue/detectors.go
Original file line number Diff line number Diff line change
Expand Up @@ -282,3 +282,29 @@ func (d *JsonQueryFunctionDetector) GetIssues() []QueryIssue {
}
return issues
}

type JsonPredicateExprDetector struct {
query string
detected bool
}

func NewJsonPredicateExprDetector(query string) *JsonPredicateExprDetector {
return &JsonPredicateExprDetector{
query: query,
}
}

func (j *JsonPredicateExprDetector) Detect(msg protoreflect.Message) error {
if queryparser.GetMsgFullName(msg) == queryparser.PG_QUERY_JSON_PREDICATE_NODE {
j.detected = true
}
return nil
}

func (d *JsonPredicateExprDetector) GetIssues() []QueryIssue {
var issues []QueryIssue
if d.detected {
issues = append(issues, NewJsonPredicateIssue(DML_QUERY_OBJECT_TYPE, "", d.query))
}
return issues
}
11 changes: 11 additions & 0 deletions yb-voyager/src/query/queryissue/detectors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -696,3 +696,14 @@ WHERE JSON_EXISTS(details, '$.price ? (@ > $price)' PASSING 30 AS price);`
assert.Equal(t, JSON_QUERY_FUNCTION, issues[0].Type, "Expected Advisory Locks issue for SQL: %s", sql)

}

func TestJsonPredicateDetector(t *testing.T) {
sql := `SELECT js, js IS JSON "json?", js IS JSON SCALAR "scalar?", js IS JSON OBJECT "object?", js IS JSON ARRAY "array?"
FROM (VALUES ('123'), ('"abc"'), ('{"a": "b"}'), ('[1,2]'),('abc')) foo(js);
`

issues := getDetectorIssues(t, NewJsonPredicateExprDetector(sql), sql)
assert.Equal(t, 1, len(issues), "Expected 1 issue for SQL: %s", sql)
assert.Equal(t, JSON_PREDICATE, issues[0].Type, "Expected Advisory Locks issue for SQL: %s", sql)

}
13 changes: 13 additions & 0 deletions yb-voyager/src/query/queryissue/issues_dml.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,16 @@ func NewLOFuntionsIssue(objectType string, objectName string, sqlStatement strin
}
return newQueryIssue(loFunctionsIssue, objectType, objectName, sqlStatement, details)
}

var jsonPredicateIssue = issue.Issue{
Type: JSON_PREDICATE,
TypeName: JSON_PREDICATE_NAME,
TypeDescription: "Postgresql 17 features not supported yet in YugabyteDB",
Suggestion: "",
GH: "",
DocsLink: "", //TODO
}

func NewJsonPredicateIssue(objectType string, objectName string, sqlStatement string) QueryIssue {
return newQueryIssue(jsonPredicateIssue, objectType, objectName, sqlStatement, map[string]interface{}{})
}
1 change: 1 addition & 0 deletions yb-voyager/src/query/queryissue/parser_issue_detector.go
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ func (p *ParserIssueDetector) genericIssues(query string) ([]QueryIssue, error)
NewRangeTableFuncDetector(query),
NewJsonConstructorFuncDetector(query),
NewJsonQueryFunctionDetector(query),
NewJsonPredicateExprDetector(query),
}

processor := func(msg protoreflect.Message) error {
Expand Down
45 changes: 30 additions & 15 deletions yb-voyager/src/query/queryissue/parser_issue_detector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,15 @@ FROM books;`,
`SELECT id, JSON_VALUE(details, '$.title') AS title
FROM books
WHERE JSON_EXISTS(details, '$.price ? (@ > $price)' PASSING 30 AS price);`,
`SELECT js, js IS JSON "json?", js IS JSON SCALAR "scalar?", js IS JSON OBJECT "object?", js IS JSON ARRAY "array?"
FROM (VALUES ('123'), ('"abc"'), ('{"a": "b"}'), ('[1,2]'),('abc')) foo(js);`,
`SELECT js,
js IS JSON OBJECT "object?",
js IS JSON ARRAY "array?",
js IS JSON ARRAY WITH UNIQUE KEYS "array w. UK?",
js IS JSON ARRAY WITHOUT UNIQUE KEYS "array w/o UK?"
FROM (VALUES ('[{"a":"1"},
{"b":"2","b":"3"}]')) foo(js);`,
}
sqlsWithExpectedIssues := map[string][]QueryIssue{
sqls[0]: []QueryIssue{
Expand Down Expand Up @@ -579,6 +588,12 @@ WHERE JSON_EXISTS(details, '$.price ? (@ > $price)' PASSING 30 AS price);`,
sqls[12]: []QueryIssue{
NewJsonQueryFunctionIssue(DML_QUERY_OBJECT_TYPE, "", sqls[12], []string{JSON_VALUE, JSON_EXISTS}),
},
sqls[13]: []QueryIssue{
NewJsonPredicateIssue(DML_QUERY_OBJECT_TYPE, "", sqls[13]),
},
sqls[14]: []QueryIssue{
NewJsonPredicateIssue(DML_QUERY_OBJECT_TYPE, "", sqls[14]),
},
}
parserIssueDetector := NewParserIssueDetector()
for stmt, expectedIssues := range sqlsWithExpectedIssues {
Expand Down Expand Up @@ -626,21 +641,21 @@ END;
$$ LANGUAGE plpgsql;`,
}
aggregateSqls := map[string][]QueryIssue{
// sqls[0]: []QueryIssue{
// NewAggregationFunctionIssue(DML_QUERY_OBJECT_TYPE, "", sqls[0], []string{"any_value"}),
// },
// sqls[1]: []QueryIssue{
// NewAggregationFunctionIssue(DML_QUERY_OBJECT_TYPE, "", sqls[1], []string{"range_intersect_agg"}),
// },
// sqls[2]: []QueryIssue{
// NewAggregationFunctionIssue(DML_QUERY_OBJECT_TYPE, "", sqls[2], []string{"range_agg"}),
// },
// sqls[3]: []QueryIssue{
// NewAggregationFunctionIssue(DML_QUERY_OBJECT_TYPE, "", sqls[3], []string{"range_intersect_agg"}),
// },
// sqls[4]: []QueryIssue{
// NewAggregationFunctionIssue(DML_QUERY_OBJECT_TYPE, "", sqls[4], []string{"range_agg"}),
// },
sqls[0]: []QueryIssue{
NewAggregationFunctionIssue(DML_QUERY_OBJECT_TYPE, "", sqls[0], []string{"any_value"}),
},
sqls[1]: []QueryIssue{
NewAggregationFunctionIssue(DML_QUERY_OBJECT_TYPE, "", sqls[1], []string{"range_intersect_agg"}),
},
sqls[2]: []QueryIssue{
NewAggregationFunctionIssue(DML_QUERY_OBJECT_TYPE, "", sqls[2], []string{"range_agg"}),
},
sqls[3]: []QueryIssue{
NewAggregationFunctionIssue(DML_QUERY_OBJECT_TYPE, "", sqls[3], []string{"range_intersect_agg"}),
},
sqls[4]: []QueryIssue{
NewAggregationFunctionIssue(DML_QUERY_OBJECT_TYPE, "", sqls[4], []string{"range_agg"}),
},
sqls[5]: []QueryIssue{
NewAggregationFunctionIssue(DML_QUERY_OBJECT_TYPE, "", "SELECT range_agg(range_value) FROM ranges;", []string{"range_agg"}),
NewAggregationFunctionIssue(DML_QUERY_OBJECT_TYPE, "", sqls[0], []string{"any_value"}),
Expand Down
1 change: 1 addition & 0 deletions yb-voyager/src/query/queryparser/traversal_proto.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ const (
PG_QUERY_JSON_FUNC_EXPR_NODE = "pg_query.JsonFuncExpr"
PG_QUERY_JSON_OBJECT_CONSTRUCTOR_NODE = "pg_query.JsonObjectConstructor"
PG_QUERY_JSON_TABLE_NODE = "pg_query.JsonTable"
PG_QUERY_JSON_PREDICATE_NODE = "pg_query.JsonIsPredicate"
)

// function type for processing nodes during traversal
Expand Down

0 comments on commit ea061d7

Please sign in to comment.