Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FETCH .. WITH TIES issue detection #2111

Merged
merged 23 commits into from
Dec 30, 2024
Merged
Show file tree
Hide file tree
Changes from 12 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
Original file line number Diff line number Diff line change
Expand Up @@ -384,4 +384,6 @@ CREATE TABLE public.locations (
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE image (title text, raster lo);
CREATE TABLE image (title text, raster lo);

CREATE TABLE employees (id INT PRIMARY KEY, salary INT);
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,10 @@ CREATE VIEW public.orders_view AS
pg_try_advisory_lock((hashtext((orders.customer_name || orders.product_name)))::bigint) AS lock_acquired,
orders.ctid AS row_ctid,
orders.xmin AS transaction_id
FROM public.orders;
FROM public.orders;

CREATE VIEW top_employees_view AS SELECT * FROM (
SELECT * FROM employees
ORDER BY salary DESC
FETCH FIRST 2 ROWS WITH TIES
) AS top_employees;
10 changes: 10 additions & 0 deletions migtests/tests/analyze-schema/expected_issues.json
Original file line number Diff line number Diff line change
Expand Up @@ -1900,5 +1900,15 @@
"Suggestion": "Large objects are not yet supported in YugabyteDB, no workaround available currently",
"GH": "https://github.com/yugabyte/yugabyte-db/issues/25318",
"MinimumVersionsFixedIn": null
},
{
"IssueType": "unsupported_features",
"ObjectType": "VIEW",
"ObjectName": "top_employees_view",
"Reason": "FETCH .. WITH TIES",
"SqlStatement": "CREATE VIEW top_employees_view AS SELECT * FROM (\n\t\t\tSELECT * FROM employees\n\t\t\tORDER BY salary DESC\n\t\t\tFETCH FIRST 2 ROWS WITH TIES\n\t\t) AS top_employees;",
"Suggestion": "No workaround available right now",
"GH": "",
"MinimumVersionsFixedIn": null
}
]
10 changes: 5 additions & 5 deletions migtests/tests/analyze-schema/summary.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@
},
{
"ObjectType": "TABLE",
"TotalCount": 51,
"TotalCount": 52,
"InvalidCount": 43,
"ObjectNames": "image, public.xml_data_example, combined_tbl1, test_arr_enum, public.locations, test_udt, combined_tbl, public.ts_query_table, public.documents, public.citext_type, public.inet_type, public.test_jsonb, test_xml_type, test_xid_type, public.range_columns_partition_test_copy, anydata_test, uritype_test, public.foreign_def_test, test_4, enum_example.bugs, table_abc, anydataset_test, unique_def_test1, test_2, table_1, public.range_columns_partition_test, table_xyz, public.users, test_3, test_5, test_7, foreign_def_test2, unique_def_test, sales_data, table_test, test_interval, test_non_pk_multi_column_list, test_9, test_8, order_details, public.employees4, anytype_test, public.meeting, test_table_in_type_file, sales, test_1, \"Test\", foreign_def_test1, salaries2, test_6, public.pr" },
"ObjectNames": "employees, image, public.xml_data_example, combined_tbl1, test_arr_enum, public.locations, test_udt, combined_tbl, public.ts_query_table, public.documents, public.citext_type, public.inet_type, public.test_jsonb, test_xml_type, test_xid_type, public.range_columns_partition_test_copy, anydata_test, uritype_test, public.foreign_def_test, test_4, enum_example.bugs, table_abc, anydataset_test, unique_def_test1, test_2, table_1, public.range_columns_partition_test, table_xyz, public.users, test_3, test_5, test_7, foreign_def_test2, unique_def_test, sales_data, table_test, test_interval, test_non_pk_multi_column_list, test_9, test_8, order_details, public.employees4, anytype_test, public.meeting, test_table_in_type_file, sales, test_1, \"Test\", foreign_def_test1, salaries2, test_6, public.pr" },
{
"ObjectType": "INDEX",
"TotalCount": 43,
Expand All @@ -50,9 +50,9 @@
},
{
"ObjectType": "VIEW",
"TotalCount": 5,
"InvalidCount": 5,
"ObjectNames": "v1, v2, test, public.orders_view, view_name"
"TotalCount": 6,
"InvalidCount": 6,
"ObjectNames": "v1, v2, test, public.orders_view, view_name, top_employees_view"
},
{
"ObjectType": "TRIGGER",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,15 @@
},
{
"ObjectType": "SEQUENCE",
"TotalCount": 27,
"TotalCount": 29,
"InvalidCount": 0,
"ObjectNames": "public.ordersentry_order_id_seq, public.\"Case_Sensitive_Columns_id_seq\", public.\"Mixed_Case_Table_Name_Test_id_seq\", public.\"Recipients_id_seq\", public.\"WITH_id_seq\", public.employees2_id_seq, public.ext_test_id_seq, public.mixed_data_types_table1_id_seq, public.mixed_data_types_table2_id_seq, public.orders2_id_seq, public.parent_table_id_seq, public.with_example1_id_seq, public.with_example2_id_seq, schema2.\"Case_Sensitive_Columns_id_seq\", schema2.\"Mixed_Case_Table_Name_Test_id_seq\", schema2.\"Recipients_id_seq\", schema2.\"WITH_id_seq\", schema2.employees2_id_seq, schema2.ext_test_id_seq, schema2.mixed_data_types_table1_id_seq, schema2.mixed_data_types_table2_id_seq, schema2.orders2_id_seq, schema2.parent_table_id_seq, schema2.with_example1_id_seq, schema2.with_example2_id_seq, test_views.view_table1_id_seq, test_views.view_table2_id_seq"
"ObjectNames": "public.ordersentry_order_id_seq, public.\"Case_Sensitive_Columns_id_seq\", public.\"Mixed_Case_Table_Name_Test_id_seq\", public.\"Recipients_id_seq\", public.\"WITH_id_seq\", public.employees2_id_seq, public.ext_test_id_seq, public.mixed_data_types_table1_id_seq, public.mixed_data_types_table2_id_seq, public.orders2_id_seq, public.parent_table_id_seq, public.with_example1_id_seq, public.with_example2_id_seq, schema2.\"Case_Sensitive_Columns_id_seq\", schema2.\"Mixed_Case_Table_Name_Test_id_seq\", schema2.\"Recipients_id_seq\", schema2.\"WITH_id_seq\", schema2.employees2_id_seq, schema2.ext_test_id_seq, schema2.mixed_data_types_table1_id_seq, schema2.mixed_data_types_table2_id_seq, schema2.orders2_id_seq, schema2.parent_table_id_seq, schema2.with_example1_id_seq, schema2.with_example2_id_seq, test_views.view_table1_id_seq, test_views.view_table2_id_seq, public.employees_id_seq, schema2.employees_id_seq"
},
{
"ObjectType": "TABLE",
"TotalCount": 66,
"TotalCount": 68,
"InvalidCount": 23,
"ObjectNames": "public.ordersentry, public.library_nested, public.orders_lateral, public.\"Case_Sensitive_Columns\", public.\"Mixed_Case_Table_Name_Test\", public.\"Recipients\", public.\"WITH\", public.audit, public.sales_region, public.boston, public.c, public.parent_table, public.child_table, public.citext_type, public.combined_tbl, public.documents, public.employees2, public.ext_test, public.foo, public.inet_type, public.london, public.mixed_data_types_table1, public.mixed_data_types_table2, public.orders, public.orders2, public.products, public.session_log, public.session_log1, public.session_log2, public.sydney, public.test_exclude_basic, public.test_jsonb, public.test_xml_type, public.ts_query_table, public.tt, public.with_example1, public.with_example2, schema2.\"Case_Sensitive_Columns\", schema2.\"Mixed_Case_Table_Name_Test\", schema2.\"Recipients\", schema2.\"WITH\", schema2.audit, schema2.sales_region, schema2.boston, schema2.c, schema2.parent_table, schema2.child_table, schema2.employees2, schema2.ext_test, schema2.foo, schema2.london, schema2.mixed_data_types_table1, schema2.mixed_data_types_table2, schema2.orders, schema2.orders2, schema2.products, schema2.session_log, schema2.session_log1, schema2.session_log2, schema2.sydney, schema2.test_xml_type, schema2.tt, schema2.with_example1, schema2.with_example2, test_views.view_table1, test_views.view_table2"
"ObjectNames": "public.ordersentry, public.library_nested, public.orders_lateral, public.\"Case_Sensitive_Columns\", public.\"Mixed_Case_Table_Name_Test\", public.\"Recipients\", public.\"WITH\", public.audit, public.sales_region, public.boston, public.c, public.parent_table, public.child_table, public.citext_type, public.combined_tbl, public.documents, public.employees2, public.ext_test, public.foo, public.inet_type, public.london, public.mixed_data_types_table1, public.mixed_data_types_table2, public.orders, public.orders2, public.products, public.session_log, public.session_log1, public.session_log2, public.sydney, public.test_exclude_basic, public.test_jsonb, public.test_xml_type, public.ts_query_table, public.tt, public.with_example1, public.with_example2, schema2.\"Case_Sensitive_Columns\", schema2.\"Mixed_Case_Table_Name_Test\", schema2.\"Recipients\", schema2.\"WITH\", schema2.audit, schema2.sales_region, schema2.boston, schema2.c, schema2.parent_table, schema2.child_table, schema2.employees2, schema2.ext_test, schema2.foo, schema2.london, schema2.mixed_data_types_table1, schema2.mixed_data_types_table2, schema2.orders, schema2.orders2, schema2.products, schema2.session_log, schema2.session_log1, schema2.session_log2, schema2.sydney, schema2.test_xml_type, schema2.tt, schema2.with_example1, schema2.with_example2, test_views.view_table1, test_views.view_table2, public.employees, schema2.employees"
},
{
"ObjectType": "INDEX",
Expand All @@ -73,9 +73,9 @@
},
{
"ObjectType": "VIEW",
"TotalCount": 7,
"InvalidCount": 3,
"ObjectNames": "public.ordersentry_view, public.sales_employees, schema2.sales_employees, test_views.v1, test_views.v2, test_views.v3, test_views.v4"
"TotalCount": 9,
"InvalidCount": 5,
"ObjectNames": "public.ordersentry_view, public.sales_employees, schema2.sales_employees, test_views.v1, test_views.v2, test_views.v3, test_views.v4, schema2.top_employees_view, public.top_employees_view"
},
{
"ObjectType": "TRIGGER",
Expand Down Expand Up @@ -555,6 +555,20 @@
}
],
"MinimumVersionsFixedIn": null
},
{
"FeatureName": "FETCH .. WITH TIES",
"Objects": [
{
"ObjectName": "public.top_employees_view",
"SqlStatement": "CREATE VIEW public.top_employees_view AS\n SELECT id,\n first_name,\n last_name,\n salary\n FROM ( SELECT employees.id,\n employees.first_name,\n employees.last_name,\n employees.salary\n FROM public.employees\n ORDER BY employees.salary DESC\n FETCH FIRST 2 ROWS WITH TIES) top_employees;"
},
{
"ObjectName": "schema2.top_employees_view",
"SqlStatement": "CREATE VIEW schema2.top_employees_view AS\n SELECT id,\n first_name,\n last_name,\n salary\n FROM ( SELECT employees.id,\n employees.first_name,\n employees.last_name,\n employees.salary\n FROM schema2.employees\n ORDER BY employees.salary DESC\n FETCH FIRST 2 ROWS WITH TIES) top_employees;"
}
],
"MinimumVersionsFixedIn": null
}
],
"UnsupportedFeaturesDesc": "Features of the source database that are not supported on the target YugabyteDB.",
Expand Down Expand Up @@ -1958,6 +1972,34 @@
"ObjectType": "",
"ParentTableName": "schema2.mixed_data_types_table1",
"SizeInBytes": 8192
},
{
"SchemaName": "public",
"ObjectName": "employees",
"RowCount": 0,
"ColumnCount": 4,
"Reads": 0,
"Writes": 0,
"ReadsPerSecond": 0,
"WritesPerSecond": 0,
"IsIndex": false,
"ObjectType": "",
"ParentTableName": null,
"SizeInBytes": 0
},
{
"SchemaName": "schema2",
"ObjectName": "employees",
"RowCount": 0,
"ColumnCount": 4,
"Reads": 0,
"Writes": 0,
"ReadsPerSecond": 0,
"WritesPerSecond": 0,
"IsIndex": false,
"ObjectType": "",
"ParentTableName": null,
"SizeInBytes": 0
}
],
"Notes": [
Expand Down
14 changes: 14 additions & 0 deletions migtests/tests/pg/assessment-report-test/pg_assessment_report.sql
Original file line number Diff line number Diff line change
Expand Up @@ -362,3 +362,17 @@ BEGIN
END IF;
END;
$$ LANGUAGE plpgsql;

-- for FETCH .. WITH TIES
CREATE TABLE employees (
id SERIAL PRIMARY KEY,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
salary NUMERIC(10, 2) NOT NULL
);

CREATE VIEW top_employees_view AS SELECT * FROM (
SELECT * FROM employees
ORDER BY salary DESC
FETCH FIRST 2 ROWS WITH TIES
) AS top_employees;
1 change: 1 addition & 0 deletions yb-voyager/cmd/assessMigrationCommand.go
Original file line number Diff line number Diff line change
Expand Up @@ -1014,6 +1014,7 @@ func fetchUnsupportedPGFeaturesFromSchemaReport(schemaAnalysisReport utils.Schem
unsupportedFeatures = append(unsupportedFeatures, getUnsupportedFeaturesFromSchemaAnalysisReport(queryissue.SYSTEM_COLUMNS_NAME, queryissue.SYSTEM_COLUMNS_NAME, "", schemaAnalysisReport, false, ""))
unsupportedFeatures = append(unsupportedFeatures, getUnsupportedFeaturesFromSchemaAnalysisReport(queryissue.LARGE_OBJECT_FUNCTIONS_NAME, queryissue.LARGE_OBJECT_FUNCTIONS_NAME, "", schemaAnalysisReport, false, ""))
unsupportedFeatures = append(unsupportedFeatures, getUnsupportedFeaturesFromSchemaAnalysisReport(REGEX_FUNCTIONS_FEATURE, "", queryissue.REGEX_FUNCTIONS, schemaAnalysisReport, false, ""))
unsupportedFeatures = append(unsupportedFeatures, getUnsupportedFeaturesFromSchemaAnalysisReport(FETCH_WITH_TIES_FEATURE, "", queryissue.FETCH_WITH_TIES, schemaAnalysisReport, false, ""))

return lo.Filter(unsupportedFeatures, func(f UnsupportedFeature, _ int) bool {
return len(f.Objects) > 0
Expand Down
1 change: 1 addition & 0 deletions yb-voyager/cmd/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ const (
BEFORE_FOR_EACH_ROW_TRIGGERS_ON_PARTITIONED_TABLE_FEATURE = "BEFORE ROW triggers on Partitioned tables"
PK_UK_CONSTRAINT_ON_COMPLEX_DATATYPES_FEATURE = "Primary / Unique key constraints on complex datatypes"
REGEX_FUNCTIONS_FEATURE = "Regex Functions"
FETCH_WITH_TIES_FEATURE = "FETCH .. WITH TIES"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: maybe "FETCH .. WITH TIES Clause"?


// Migration caveats

Expand Down
1 change: 1 addition & 0 deletions yb-voyager/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ require (
golang.org/x/term v0.24.0
google.golang.org/api v0.169.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gotest.tools/v3 v3.5.1
)

require (
Expand Down
1 change: 0 additions & 1 deletion yb-voyager/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2986,7 +2986,6 @@ gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
Expand Down
4 changes: 2 additions & 2 deletions yb-voyager/src/query/queryissue/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ const (
ADVISORY_LOCKS_NAME = "Advisory Locks"
SYSTEM_COLUMNS_NAME = "System Columns"
XML_FUNCTIONS_NAME = "XML Functions"

REGEX_FUNCTIONS = "REGEX_FUNCTIONS"
FETCH_WITH_TIES = "FETCH_WITH_TIES"
REGEX_FUNCTIONS = "REGEX_FUNCTIONS"
)

// Object types
Expand Down
47 changes: 47 additions & 0 deletions yb-voyager/src/query/queryissue/detectors.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,12 @@ limitations under the License.
package queryissue

import (
"fmt"

mapset "github.com/deckarep/golang-set/v2"
pg_query "github.com/pganalyze/pg_query_go/v6"
log "github.com/sirupsen/logrus"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"

"github.com/yugabyte/yb-voyager/yb-voyager/src/query/queryparser"
Expand Down Expand Up @@ -195,3 +199,46 @@ func (d *RangeTableFuncDetector) GetIssues() []QueryIssue {
}
return issues
}

type SelectStmtDetector struct {
query string
limitOptionWithTiesDetected bool
}

func NewSelectStmtDetector(query string) *SelectStmtDetector {
return &SelectStmtDetector{
query: query,
}
}

func (d *SelectStmtDetector) getSelectStmtFromProto(msg protoreflect.Message) (*pg_query.SelectStmt, error) {
protoMsg := msg.Interface().(proto.Message)
selectStmtNode, ok := protoMsg.(*pg_query.SelectStmt)
if !ok {
return nil, fmt.Errorf("failed to cast %s to %s", queryparser.PG_QUERY_SELECTSTMT_NODE, queryparser.PG_QUERY_SELECTSTMT_NODE)
}
return selectStmtNode, nil
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets add this function inhelper_protomsg.go


func (d *SelectStmtDetector) Detect(msg protoreflect.Message) error {
if queryparser.GetMsgFullName(msg) == queryparser.PG_QUERY_SELECTSTMT_NODE {
selectStmtNode, err := d.getSelectStmtFromProto(msg)
if err != nil {
return err
}
// checks if a SelectStmt node uses a FETCH clause with TIES
// https://www.postgresql.org/docs/13/sql-select.html#SQL-LIMIT
if selectStmtNode.LimitOption == pg_query.LimitOption_LIMIT_OPTION_WITH_TIES {
Copy link
Contributor

@priyanshi-yb priyanshi-yb Dec 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

define this limit option const pg_query.LimitOption_LIMIT_OPTION_WITH_TIES in queryparser
After these two changes (this and the above one), you won't need to use the pg_query package. Because we already have queryparser pkg for parser related stuff. So I think we should not use the pg_query directly here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. Don't think we can completely avoid this because the return type would be a pg_query.SelectStmt. But we probably don't need to have an explicit dependency.

But broadly speaking, agree with not preferring to use pg_query directly here. (as much as possilble).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, as much as possible we should avoid using it directly in the queryissue.

d.limitOptionWithTiesDetected = true
}
}
return nil
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am thinking can we stick to one of the approaches (protomsg) as a primary approach, and use other one where its significantly making things easy, other code might become a bit messy.
There is a overhead of conversion also comes.

Here in this case i guess something like GetStringField() or GetEnumField() we already have, can be used to get pg_query.LimitOption_LIMIT_OPTION_WITH_TIES

Copy link
Collaborator Author

@makalaaneesh makalaaneesh Dec 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see your point about consistency. But why, in general, do you prefer accessing fields of a message via protoreflect.Message rather than structs directly?

There is a overhead of conversion also comes.

I doubt there is significant overhead. It's just type assertion.

Copy link
Collaborator Author

@makalaaneesh makalaaneesh Dec 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Merged this for now, since it's been pending for a while and I had to resolve a lot of conflicts!
But let's discuss this nonetheless.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But why, in general, do you prefer accessing fields of a message via protoreflect.Message rather than structs directly?

@makalaaneesh
Couple of reasons:
The field name we use to fetch from the protomsg is exactly the same as visible in a parse tree output.
In case of structs, you need to figureout the struct field which gives you the required info(sligtly different named)

Consistency here is another reason.


func (d *SelectStmtDetector) GetIssues() []QueryIssue {
var issues []QueryIssue
if d.limitOptionWithTiesDetected {
issues = append(issues, NewFetchWithTiesIssue(DML_QUERY_OBJECT_TYPE, "", d.query))
}
return issues
}
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 @@ -82,3 +82,16 @@ var loFunctionsIssue = issue.Issue{
func NewLOFuntionsIssue(objectType string, objectName string, sqlStatement string) QueryIssue {
return newQueryIssue(loFunctionsIssue, objectType, objectName, sqlStatement, map[string]interface{}{})
}

var fetchWithTiesIssue = issue.Issue{
Type: FETCH_WITH_TIES,
TypeName: "FETCH .. WITH TIES",
TypeDescription: "FETCH .. WITH TIES is not supported in YugabyteDB",
Suggestion: "No workaround available right now",
GH: "",
DocsLink: "", //TODO
}

func NewFetchWithTiesIssue(objectType string, objectName string, sqlStatement string) QueryIssue {
return newQueryIssue(fetchWithTiesIssue, objectType, objectName, sqlStatement, map[string]interface{}{})
}
22 changes: 22 additions & 0 deletions yb-voyager/src/query/queryissue/issues_dml_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,25 @@ func testRegexFunctionsIssue(t *testing.T) {
}
}

func testFetchWithTiesIssue(t *testing.T) {
ctx := context.Background()
conn, err := getConn()
assert.NoError(t, err)

defer conn.Close(context.Background())

stmts := []string{
`SELECT * FROM employees
ORDER BY salary DESC
FETCH FIRST 2 ROWS WITH TIES;`,
}

for _, stmt := range stmts {
_, err = conn.Exec(ctx, stmt)
assertErrorCorrectlyThrownForIssueForYBVersion(t, err, `syntax error at or near "WITH"`, regexFunctionsIssue)
}
}

func TestDMLIssuesInYBVersion(t *testing.T) {
var err error
ybVersion := os.Getenv("YB_VERSION")
Expand Down Expand Up @@ -90,4 +109,7 @@ func TestDMLIssuesInYBVersion(t *testing.T) {
success = t.Run(fmt.Sprintf("%s-%s", "regex functions", ybVersion), testRegexFunctionsIssue)
assert.True(t, success)

success = t.Run(fmt.Sprintf("%s-%s", "fetch with ties", ybVersion), testFetchWithTiesIssue)
assert.True(t, success)

}
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 @@ -375,6 +375,7 @@ func (p *ParserIssueDetector) genericIssues(query string) ([]QueryIssue, error)
NewColumnRefDetector(query),
NewXmlExprDetector(query),
NewRangeTableFuncDetector(query),
NewSelectStmtDetector(query),
}

processor := func(msg protoreflect.Message) error {
Expand Down
Loading
Loading