diff --git a/src/dsql/dsql.h b/src/dsql/dsql.h index 7dac1ae7b7..25f283290c 100644 --- a/src/dsql/dsql.h +++ b/src/dsql/dsql.h @@ -481,6 +481,7 @@ public: void addCTEs(dsql_nod* list); dsql_nod* findCTE(const dsql_str* name); void clearCTEs(); + void checkUnusedCTEs(); // hvlad: each member of recursive CTE can refer to CTE itself (only once) via // CTE name or via alias. We need to substitute this aliases when processing CTE diff --git a/src/dsql/node.h b/src/dsql/node.h index d6ce072ba6..ca208dc86b 100644 --- a/src/dsql/node.h +++ b/src/dsql/node.h @@ -1055,6 +1055,7 @@ enum nod_flags_vals { NOD_CURSOR_ALL = USHORT(-1U), NOD_DT_IGNORE_COLUMN_CHECK = 1, // nod_cursor, see pass1_cursor_name + NOD_DT_CTE_USED = 2, // nod_derived_table NOD_PERMANENT_TABLE = 1, // nod_def_relation NOD_GLOBAL_TEMP_TABLE_PRESERVE_ROWS = 2, diff --git a/src/dsql/pass1.cpp b/src/dsql/pass1.cpp index 097b8c4e94..3891f58905 100644 --- a/src/dsql/pass1.cpp +++ b/src/dsql/pass1.cpp @@ -771,6 +771,8 @@ dsql_nod* PASS1_node(dsql_req* request, dsql_nod* input, bool proc_flag) dsql_nod* cte = request->findCTE(rel_name); if (cte) { + cte->nod_flags |= NOD_DT_CTE_USED; + if ((request->req_flags & REQ_CTE_recursive) && request->req_curr_ctes.hasData() && (request->req_curr_ctes.object() == cte)) @@ -7641,7 +7643,10 @@ static dsql_nod* pass1_rse_impl( dsql_req* request, dsql_nod* input, dsql_nod* o update_lock, input->nod_flags); if (node_with) + { + request->checkUnusedCTEs(); request->clearCTEs(); + } return ret; } @@ -10390,6 +10395,22 @@ void dsql_req::clearCTEs() } +void dsql_req::checkUnusedCTEs() +{ + for (size_t i = 0; i < req_ctes.getCount(); i++) + { + dsql_nod* cte = req_ctes[i]; + if (!(cte->nod_flags & NOD_DT_CTE_USED)) + { + const dsql_str* cte_name = (dsql_str*) cte->nod_arg[e_derived_table_alias]; + + ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104, + isc_arg_gds, isc_dsql_cte_not_used, + isc_arg_string, ERR_cstring(cte_name->str_data), 0); + } + } +} + // Returns false for hidden fields and true for non-hidden. // For non-hidden, change "node" if the field is part of an // implicit join. diff --git a/src/msgs/facilities2.sql b/src/msgs/facilities2.sql index 6821128912..16b51fdaf2 100644 --- a/src/msgs/facilities2.sql +++ b/src/msgs/facilities2.sql @@ -18,7 +18,7 @@ set bulk_insert INSERT INTO FACILITIES (LAST_CHANGE, FACILITY, FAC_CODE, MAX_NUM ('1996-11-07 13:39:40', 'INSTALL', 10, 1) ('1996-11-07 13:38:41', 'TEST', 11, 4) ('2007-12-21 13:11:00', 'GBAK', 12, 302) -('2007-11-07 12:33:00', 'SQLERR', 13, 951) +('2008-03-17 12:05:00', 'SQLERR', 13, 952) ('1996-11-07 13:38:42', 'SQLWARN', 14, 613) ('2006-09-10 03:04:31', 'JRD_BUGCHK', 15, 307) -- diff --git a/src/msgs/messages2.sql b/src/msgs/messages2.sql index 9b0daf774e..f10c091e35 100644 --- a/src/msgs/messages2.sql +++ b/src/msgs/messages2.sql @@ -2568,8 +2568,9 @@ constrained - no 2 table rows can have duplicate column values', NULL, NULL); -- Write the new SQLERR messages here. ('dsql_col_more_than_once_using', 'pass1_join', 'dsql.cpp', NULL, 13, 947, NULL, 'column @1 appears more than once in USING clause', NULL, NULL); ('dsql_unsupp_feature_dialect', NULL, NULL, NULL, 13, 948, NULL, 'feature is not supported in dialect @1', NULL, NULL); -('dsql_col_more_than_once_view', 'define_view', 'ddl.cpp', NULL, 13, 949, NULL, 'column @1 appears more than once in ALTER VIEW', NULL, NULL); -('dsql_unsupported_in_auto_trans', 'PASS1_statement', 'pass1.cpp', NULL, 13, 950, NULL, '@1 is not supported inside IN AUTONOMOUS TRANSACTION block', NULL, NULL); +('dsql_cte_not_used', 'dsql_req::checkUnusedCTEs', 'pass1.cpp', NULL, 13, 949, NULL, 'CTE "@1" is not used in query', NULL, NULL); +('dsql_col_more_than_once_view', 'define_view', 'ddl.cpp', NULL, 13, 950, NULL, 'column @1 appears more than once in ALTER VIEW', NULL, NULL); +('dsql_unsupported_in_auto_trans', 'PASS1_statement', 'pass1.cpp', NULL, 13, 951, NULL, '@1 is not supported inside IN AUTONOMOUS TRANSACTION block', NULL, NULL); -- SQLWARN (NULL, NULL, NULL, NULL, 14, 100, NULL, 'Row not found for fetch, update or delete, or the result of a query is an empty table.', NULL, NULL); (NULL, NULL, NULL, NULL, 14, 101, NULL, 'segment buffer length shorter than expected', NULL, NULL); diff --git a/src/msgs/system_errors2.sql b/src/msgs/system_errors2.sql index d37fcbccab..9829eff568 100644 --- a/src/msgs/system_errors2.sql +++ b/src/msgs/system_errors2.sql @@ -799,8 +799,9 @@ COMMIT WORK; (-104, '42', '000', 13, 946, 'dsql_cte_nested_with', NULL, NULL) (-104, '42', '000', 13, 947, 'dsql_col_more_than_once_using', NULL, NULL) (-901, 'HY', '000', 13, 948, 'dsql_unsupp_feature_dialect', NULL, NULL) -(-104, '42', '000', 13, 949, 'dsql_col_more_than_once_view', NULL, NULL) -(-901, 'HY', '000', 13, 950, 'dsql_unsupported_in_auto_trans', NULL, NULL) +(-104, '42', '000', 13, 949, 'dsql_cte_not_used', NULL, NULL) +(-104, '42', '000', 13, 950, 'dsql_col_more_than_once_view', NULL, NULL) +(-901, 'HY', '000', 13, 951, 'dsql_unsupported_in_auto_trans', NULL, NULL) -- GSEC (-901, '00', '000', 18, 15, 'gsec_cant_open_db', NULL, NULL) (-901, '00', '000', 18, 16, 'gsec_switches_error', NULL, NULL)