Skip to content

Commit f3d6174

Browse files
committed
MB-72184: add UDF drop regression test (bug fixed)
Add regression test test_system_functions_after_drop_no_key_not_found to verify dropped functions are not returned in system:functions and no key-not-found behavior occurs. Also include the test in py-tuq-udf.conf. Change-Id: Iaf1d57e88639a8cca8671afe8380ba035709f356 Reviewed-on: https://review.couchbase.org/c/testrunner/+/247171 Reviewed-by: <pavan.pb@couchbase.com> Tested-by: <veena.k@couchbase.com>
1 parent 20b02d0 commit f3d6174

6 files changed

Lines changed: 155 additions & 0 deletions

File tree

conf/tuq/py-early-filter.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,4 @@ tuqquery.tuq_early_filter_order.QueryEarlyFilterTests:
3232
test_covering_in_early_filter
3333
test_MB63069
3434
test_MB_66614
35+
test_early_filter_applied_on_indexscan_for_join_predicates

conf/tuq/py-tuq-ext-sanity.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ tuqquery.tuq_sanity.QuerySanityTests:
1717
test_where_millis,GROUP=DATE;P0;CE
1818
test_order_by_dates,GROUP=DATE;P0;CE
1919
test_MB63998,GROUP=P0
20+
test_MB70834_union_scan_for_or_predicate_with_partial_indexes,GROUP=P0
2021
test_slice_filter,GROUP=P0
2122
tuqquery.tuq_update_statistics.QueryUpdateStatsTests:
2223
test_update_stats_index_multi,bucket_size=300,bucket_name=travel-sample,load_sample=True,GROUP=NON_CE

conf/tuq/py-tuq-udf.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ tuqquery.tuq_UDF.QueryUDFTests:
1414
test_inline_create_or_replace,GROUP=P1
1515
test_system_functions_create_and_replace,GROUP=P1
1616
test_system_functions_drop,GROUP=P1
17+
test_system_functions_after_drop_no_key_not_found,GROUP=P1
1718
test_inline_query_function,GROUP=P1
1819
test_inline_query_function_no_index,GROUP=P1
1920
test_inline_query_function_syntax_error,GROUP=P1

pytests/tuqquery/tuq_UDF.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,43 @@ def test_system_functions_drop(self):
628628
self.log.error(str(e))
629629
self.fail()
630630

631+
def test_system_functions_after_drop_no_key_not_found(self):
632+
"""MB-72184: Verify dropped UDFs are not returned in system:functions and
633+
do not cause key-not-found errors after deletion."""
634+
function_names = [
635+
"func_global_mb72184",
636+
"func_scope_a_mb72184",
637+
"func_scope_b_mb72184",
638+
]
639+
try:
640+
self.run_cbq_query("CREATE FUNCTION `default`:`{0}`() {{ 0 }}".format(function_names[0]))
641+
self.run_cbq_query("CREATE FUNCTION `default`:`default`.`_default`.`{0}`() {{ 0 }}".format(function_names[1]))
642+
self.run_cbq_query("CREATE FUNCTION `default`:`default`.`_default`.`{0}`() {{ 0 }}".format(function_names[2]))
643+
644+
self.run_cbq_query("DROP FUNCTION `default`:`{0}`".format(function_names[0]))
645+
self.run_cbq_query("DROP FUNCTION `default`:`default`.`_default`.`{0}`".format(function_names[1]))
646+
self.run_cbq_query("DROP FUNCTION `default`:`default`.`_default`.`{0}`".format(function_names[2]))
647+
648+
for _ in range(5):
649+
result = self.run_cbq_query("SELECT f.identity.name FROM system:functions AS f ORDER BY f.identity.name")
650+
returned_names = [row.get("name") for row in result.get("results", []) if isinstance(row, dict)]
651+
for fn in function_names:
652+
self.assertTrue(fn not in returned_names, "{0} still present in system:functions".format(fn))
653+
except Exception as e:
654+
self.log.error(str(e))
655+
self.fail()
656+
finally:
657+
drop_queries = [
658+
"DROP FUNCTION `default`:`{0}` IF EXISTS".format(function_names[0]),
659+
"DROP FUNCTION `default`:`default`.`_default`.`{0}` IF EXISTS".format(function_names[1]),
660+
"DROP FUNCTION `default`:`default`.`_default`.`{0}` IF EXISTS".format(function_names[2]),
661+
]
662+
for query in drop_queries:
663+
try:
664+
self.run_cbq_query(query)
665+
except Exception as e:
666+
self.log.error(str(e))
667+
631668
def test_inline_query_function(self):
632669
try:
633670
self.run_cbq_query("CREATE OR REPLACE FUNCTION func1(nameval) {{ (select * from default:default.{0}.{1} where name = nameval) }}".format(self.scope,self.collections[0]))

pytests/tuqquery/tuq_early_filter_order.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,3 +1003,75 @@ def test_MB64917(self):
10031003
self.assertEqual(result['results'], expected)
10041004
self.assertTrue('index_keys' not in str(plan), "We expect early filter order not to occur please check plan: {plan}")
10051005
self.assertTrue(operator1 == 'IndexScan3' and operator2 != 'Order')
1006+
1007+
def test_early_filter_applied_on_indexscan_for_join_predicates(self):
1008+
"""MB-70928: Verify early filter is applied on index scan for join predicates."""
1009+
idx1 = "idx_mb70928_ix1"
1010+
idx3 = "idx_mb70928_ix3"
1011+
doc_m = "mb70928_m"
1012+
doc_cfl = "mb70928_cfl"
1013+
1014+
def _find_m_index_scan(plan_node):
1015+
if isinstance(plan_node, dict):
1016+
if (plan_node.get("#operator") == "IndexScan3" and
1017+
plan_node.get("as") == "m" and
1018+
plan_node.get("index") == idx1):
1019+
return plan_node
1020+
for key in ("~children", "scans"):
1021+
child_nodes = plan_node.get(key, [])
1022+
if isinstance(child_nodes, list):
1023+
for child in child_nodes:
1024+
found = _find_m_index_scan(child)
1025+
if found:
1026+
return found
1027+
if "~child" in plan_node:
1028+
found = _find_m_index_scan(plan_node["~child"])
1029+
if found:
1030+
return found
1031+
elif isinstance(plan_node, list):
1032+
for child in plan_node:
1033+
found = _find_m_index_scan(child)
1034+
if found:
1035+
return found
1036+
return None
1037+
1038+
try:
1039+
self.run_cbq_query(
1040+
"UPSERT INTO default VALUES "
1041+
"('{0}', {{'c1':'Customer','c2':'US_KEY','a1':[{{'RteID':'USF2OP','EffDtRnge':{{'StrtDt':'1000-01-01','EndDt':'9999-12-31'}}}}]}}), "
1042+
"('{1}', {{'c3':'US_KEY'}})".format(doc_m, doc_cfl)
1043+
)
1044+
self.run_cbq_query("CREATE INDEX {0} ON default(c1,a1,c2)".format(idx1))
1045+
self.run_cbq_query("CREATE INDEX {0} ON default(c3)".format(idx3))
1046+
self.wait_for_all_indexes_online()
1047+
1048+
explain_query = (
1049+
"EXPLAIN SELECT COUNT(1) "
1050+
"FROM default AS m "
1051+
"LEFT JOIN default cfl ON m.c2 = cfl.c3 "
1052+
"WHERE m.c1 = 'Customer' "
1053+
"AND (ANY c IN m.a1 SATISFIES c.RteID = 'USF2OP' "
1054+
"AND NOW_LOCAL('1111-11-11') BETWEEN c.EffDtRnge.StrtDt AND c.EffDtRnge.EndDt END)"
1055+
)
1056+
explain = self.run_cbq_query(explain_query)
1057+
plan = explain['results'][0]['plan']
1058+
m_scan = _find_m_index_scan(plan)
1059+
self.assertIsNotNone(m_scan, f"Expected IndexScan3 for alias m on {idx1}, plan: {plan}")
1060+
self.assertTrue("filter" in m_scan, f"Expected early filter in IndexScan3, scan: {m_scan}")
1061+
scan_filter = m_scan["filter"]
1062+
self.assertTrue("Customer" in scan_filter, f"Expected c1 predicate in scan filter: {scan_filter}")
1063+
self.assertTrue("RteID" in scan_filter and "any" in scan_filter.lower(),
1064+
f"Expected ANY predicate in scan filter: {scan_filter}")
1065+
finally:
1066+
try:
1067+
self.run_cbq_query("DROP INDEX default.{0}".format(idx1))
1068+
except Exception:
1069+
pass
1070+
try:
1071+
self.run_cbq_query("DROP INDEX default.{0}".format(idx3))
1072+
except Exception:
1073+
pass
1074+
try:
1075+
self.run_cbq_query("DELETE FROM default USE KEYS ['{0}','{1}']".format(doc_m, doc_cfl))
1076+
except Exception:
1077+
pass

pytests/tuqquery/tuq_sanity.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3094,6 +3094,49 @@ def test_explain_union(self, index):
30943094
else:
30953095
self.log.info("Query is not using specified index")
30963096

3097+
def test_MB70834_union_scan_for_or_predicate_with_partial_indexes(self):
3098+
"""MB-70834: Verify union scan is correctly applied for OR predicates
3099+
with partial indexes."""
3100+
self.fail_if_no_buckets()
3101+
bucket = self.query_buckets[0]
3102+
idx_us = "idx_mb70834_us"
3103+
idx_uk = "idx_mb70834_uk"
3104+
doc_us = "mb70834_doc_us"
3105+
doc_uk = "mb70834_doc_uk"
3106+
try:
3107+
self.run_cbq_query(
3108+
"UPSERT INTO {0} (KEY, VALUE) VALUES ('{1}', {{'country':'US'}}), ('{2}', {{'country':'UK'}})".format(
3109+
bucket, doc_us, doc_uk
3110+
)
3111+
)
3112+
self.run_cbq_query("CREATE INDEX {0} ON {1}(country) WHERE country = 'US'".format(idx_us, bucket))
3113+
self.run_cbq_query("CREATE INDEX {0} ON {1}(country) WHERE country = 'UK'".format(idx_uk, bucket))
3114+
self.wait_for_all_indexes_online()
3115+
3116+
explain = self.run_cbq_query(
3117+
"EXPLAIN SELECT META().id FROM {0} WHERE country = 'US' OR country = 'UK'".format(bucket)
3118+
)
3119+
explain_str = str(explain)
3120+
self.assertTrue("UnionScan" in explain_str, "UnionScan is not used in explain plan")
3121+
self.assertTrue(idx_us in explain_str and idx_uk in explain_str,
3122+
"Expected partial indexes are not present in explain plan")
3123+
except Exception as e:
3124+
self.log.error(str(e))
3125+
self.fail()
3126+
finally:
3127+
try:
3128+
self.run_cbq_query("DROP INDEX {0} ON {1}".format(idx_us, bucket))
3129+
except Exception:
3130+
pass
3131+
try:
3132+
self.run_cbq_query("DROP INDEX {0} ON {1}".format(idx_uk, bucket))
3133+
except Exception:
3134+
pass
3135+
try:
3136+
self.run_cbq_query("DELETE FROM {0} USE KEYS ['{1}','{2}']".format(bucket, doc_us, doc_uk))
3137+
except Exception:
3138+
pass
3139+
30973140
##############################################################################################
30983141
#
30993142
# DATETIME

0 commit comments

Comments
 (0)