ORA-02070: Database Does Not Support Operation - Fix Remote SQL
ORA-02070: Database Does Not Support Operation in This Context
Section titled “ORA-02070: Database Does Not Support Operation in This Context”Error Overview
Section titled “Error Overview”Error Text: ORA-02070: database string does not support string in this context
The ORA-02070 error is raised when Oracle attempts to push an operation or SQL construct to a remote database through a database link, but the remote database (or the Oracle Gateway mediating the connection) does not support that operation. The error message includes the name of the remote database and the specific operation or SQL function that was rejected.
This error is most common in heterogeneous environments where Oracle connects to non-Oracle databases (SQL Server, MySQL, PostgreSQL, IBM Db2, Sybase, etc.) via Oracle Database Gateway (formerly Oracle Transparent Gateway or Generic Connectivity/ODBC). However, it can also occur between two Oracle databases when one is significantly older than the other and a newer SQL feature is pushed down to an older remote Oracle instance.
Unlike ORA-02064 which signals a categorically unsupported operation type, ORA-02070 is specifically about a particular SQL function, operator, or construct that the remote database’s query engine does not recognize or cannot execute.
Common Causes
Section titled “Common Causes”1. Oracle-Specific Functions Pushed to Non-Oracle Databases
Section titled “1. Oracle-Specific Functions Pushed to Non-Oracle Databases”- Functions like
DECODE,NVL,TO_DATE,TRUNC,SYSDATE,ROWNUMare Oracle-specific - When Oracle’s query optimizer “pushes” a WHERE clause or SELECT expression to the remote database, the remote engine cannot recognize these functions
- The remote database (SQL Server, MySQL, etc.) uses different function names and syntax
2. Oracle Analytic and Window Functions Over Heterogeneous Links
Section titled “2. Oracle Analytic and Window Functions Over Heterogeneous Links”ROW_NUMBER() OVER (...),RANK() OVER (...),LAG(),LEAD()may not be translatable by the gateway- Even if the remote database supports window functions natively, the gateway may not translate the syntax
- Oracle 23ai JSON functions and new SQL features are unlikely to be translatable via older gateways
3. Oracle SQL Syntax Not Supported by the Gateway Translation Layer
Section titled “3. Oracle SQL Syntax Not Supported by the Gateway Translation Layer”CONNECT BYhierarchical queries cannot be translated for non-Oracle databasesMODELclause,PIVOT,UNPIVOTare Oracle-specificMERGEstatement syntax differs significantly between databases and may not translateRETURNINGclause in DML statements
4. Data Type Incompatibilities
Section titled “4. Data Type Incompatibilities”- Mapping Oracle
NUMBERto the remote database’s numeric types may fail for edge cases - Oracle
DATE(which includes time) vs SQL ServerDATE(date only) creates translation issues - Oracle
VARCHAR2semantics differ from SQL ServerVARCHARin null handling and trailing spaces CLOB,BLOB, andXMLTYPEhave no universal mapping in heterogeneous gateways
5. Older Remote Oracle Database Version
Section titled “5. Older Remote Oracle Database Version”- Oracle 19c local database attempting to push
LATERALjoin syntax to a remote 11g database - New SQL features introduced in 12c/18c/19c/21c/23ai may be rejected by older remote Oracle instances
- Optimizer query transformation pushing a new construct that an older database cannot parse
6. Capability Limitations of the Specific Oracle Gateway
Section titled “6. Capability Limitations of the Specific Oracle Gateway”- Oracle Database Gateway for SQL Server has a defined capability table; unsupported functions trigger ORA-02070
- Oracle Database Gateway for ODBC (Generic Connectivity) has the most restrictive capability set
- Some capabilities are disabled by default in gateway
initORACLE_SID.oraand must be explicitly enabled
Diagnostic Queries
Section titled “Diagnostic Queries”Identify the Remote Database and Link
Section titled “Identify the Remote Database and Link”-- Find which database link caused the errorSELECT owner, db_link, username, host, createdFROM dba_db_linksWHERE db_link = UPPER('failing_link_name');
-- Check if it is a heterogeneous (non-Oracle) linkSELECT db_link, username, host, CASE WHEN UPPER(host) LIKE '%HS%' THEN 'Likely Heterogeneous' WHEN UPPER(host) LIKE '%GW%' THEN 'Likely Gateway' WHEN UPPER(host) LIKE '%ODBC%' THEN 'ODBC Gateway' WHEN UPPER(host) LIKE '%SQLSVR%' THEN 'SQL Server Gateway' ELSE 'Oracle or Unknown' END AS link_typeFROM dba_db_linksORDER BY db_link;Query Remote Database Capabilities (Heterogeneous Links)
Section titled “Query Remote Database Capabilities (Heterogeneous Links)”-- View what the remote database/gateway supportsSELECT capability_name, capability_description, is_supported, is_updateableFROM v$hs_capability@failing_link_nameORDER BY is_supported DESC, capability_name;
-- Check specific capabilitySELECT capability_name, is_supportedFROM v$hs_capability@failing_link_nameWHERE capability_name IN ( 'QUERY_REWRITE_CAPABLE', 'JOIN_CAPABLE', 'SUBQUERY_CAPABLE', 'LIKE_OPERATOR', 'NOT_LIKE_OPERATOR', 'GROUP_BY_CAPABLE', 'ORDER_BY_CAPABLE', 'FUNCTIONS', 'PROCEDURE_CALLS');Identify the Specific SQL Being Rejected
Section titled “Identify the Specific SQL Being Rejected”-- Find the SQL that triggered ORA-02070SELECT sql_id, SUBSTR(sql_text, 1, 500) AS sql_text, executions, last_active_time, parsing_schema_nameFROM v$sqlWHERE sql_text LIKE '%@failing_link_name%' AND last_active_time > SYSDATE - 1/24ORDER BY last_active_time DESCFETCH FIRST 10 ROWS ONLY;Verify Remote Oracle Database Version (for Oracle-to-Oracle Issues)
Section titled “Verify Remote Oracle Database Version (for Oracle-to-Oracle Issues)”-- Check remote Oracle versionSELECT banner FROM v$version@remote_oracle_link;
-- More detailed version informationSELECT version, version_full, startup_timeFROM v$instance@remote_oracle_link;Check the Heterogeneous Services Data Dictionary
Section titled “Check the Heterogeneous Services Data Dictionary”-- View heterogeneous services parameters for the linkSELECT param_name, param_valueFROM v$hs_parameter@failing_link_nameORDER BY param_name;
-- View HS session informationSELECT hs_session_id, db_link, agent_name, machine, program, logon_timeFROM v$hs_sessionORDER BY logon_time DESC;Check Alert Log for Gateway Errors
Section titled “Check Alert Log for Gateway Errors”-- Review recent ORA-02070 occurrences in the alert logSELECT originating_timestamp, message_textFROM v$diag_alert_extWHERE message_text LIKE '%ORA-02070%' OR message_text LIKE '%heterogeneous%' OR message_text LIKE '%gateway%'ORDER BY originating_timestamp DESCFETCH FIRST 30 ROWS ONLY;Step-by-Step Resolution
Section titled “Step-by-Step Resolution”1. Identify the Exact Operation Oracle Is Trying to Push Down
Section titled “1. Identify the Exact Operation Oracle Is Trying to Push Down”Enable heterogeneous services tracing to see exactly what SQL Oracle sends to the remote database:
-- Enable gateway tracing for the sessionALTER SESSION SET events '10841 trace name context forever, level 3';
-- Run the failing querySELECT col1 FROM remote_table@failing_link WHERE TRUNC(date_col) = TRUNC(SYSDATE);
-- Turn off tracingALTER SESSION SET events '10841 trace name context off';
-- Find the trace fileSELECT value FROM v$parameter WHERE name = 'diagnostic_dest';Review the trace file to see which specific function or construct the gateway rejected.
2. Rewrite the Query to Avoid Oracle-Specific Functions
Section titled “2. Rewrite the Query to Avoid Oracle-Specific Functions”Replace Oracle-specific functions with portable equivalents or move the computation to the local database:
-- BAD: Oracle-specific TRUNC pushed to remote SQL ServerSELECT col1FROM remote_table@sqlserver_linkWHERE TRUNC(date_col) = TRUNC(SYSDATE);
-- GOOD: Use ANSI-compatible expressions, or compute locally and bind as a valueSELECT col1FROM remote_table@sqlserver_linkWHERE date_col >= TRUNC(SYSDATE) AND date_col < TRUNC(SYSDATE) + 1;
-- BETTER: Bind the dates to prevent pushdown of the functionSELECT col1FROM remote_table@sqlserver_linkWHERE date_col >= :v_start_date AND date_col < :v_end_date;3. Use the NO_MERGE or NO_PUSH_PRED Hints to Prevent Pushdown
Section titled “3. Use the NO_MERGE or NO_PUSH_PRED Hints to Prevent Pushdown”Oracle’s optimizer may push predicates or expressions to the remote database automatically. Hints can prevent this:
-- Force Oracle to fetch all data locally and apply the filter locallySELECT /*+ NO_MERGE(r) NO_PUSH_PRED(r) */ col1FROM (SELECT col1, date_col FROM remote_table@failing_link) rWHERE TRUNC(r.date_col) = TRUNC(SYSDATE);By wrapping the remote query in an inline view and preventing merge/predicate push, Oracle fetches the raw data and applies the Oracle-specific function locally.
4. Use DBMS_HS_PASSTHROUGH for Native Remote SQL
Section titled “4. Use DBMS_HS_PASSTHROUGH for Native Remote SQL”For non-Oracle databases, use passthrough SQL to send the remote database’s native SQL directly:
DECLARE v_cursor INTEGER; v_col1 VARCHAR2(200); v_rows INTEGER;BEGIN v_cursor := DBMS_HS_PASSTHROUGH.OPEN_CURSOR@sqlserver_link;
-- Send SQL Server native syntax directly, bypassing Oracle translation DBMS_HS_PASSTHROUGH.PARSE@sqlserver_link( v_cursor, 'SELECT col1 FROM remote_table WHERE CAST(date_col AS DATE) = CAST(GETDATE() AS DATE)' );
LOOP v_rows := DBMS_HS_PASSTHROUGH.FETCH_ROW@sqlserver_link(v_cursor, FALSE); EXIT WHEN v_rows = 0;
DBMS_HS_PASSTHROUGH.GET_VALUE@sqlserver_link(v_cursor, 1, v_col1); DBMS_OUTPUT.PUT_LINE('col1: ' || v_col1); END LOOP;
DBMS_HS_PASSTHROUGH.CLOSE_CURSOR@sqlserver_link(v_cursor);END;/5. Create a View on the Remote Database to Abstract Complex Logic
Section titled “5. Create a View on the Remote Database to Abstract Complex Logic”For non-Oracle remote databases, create a view in the remote database that pre-computes complex expressions using the remote database’s native functions:
-- On the REMOTE SQL Server database (created by the SQL Server DBA):-- CREATE VIEW oracle_accessible_view AS-- SELECT-- id,-- col1,-- CAST(date_col AS DATE) AS date_only,-- CONVERT(VARCHAR(10), date_col, 120) AS date_string-- FROM remote_table;
-- On the LOCAL Oracle database: query the view through the link (no functions to translate)SELECT col1FROM oracle_accessible_view@sqlserver_linkWHERE date_string = TO_CHAR(SYSDATE, 'YYYY-MM-DD');6. Configure Gateway Capabilities in the Init File
Section titled “6. Configure Gateway Capabilities in the Init File”For Oracle Gateway, some capabilities can be explicitly configured in the gateway initialization file (init<SID>.ora):
# In $ORACLE_HOME/hs/admin/initSQLSVR.ora (example for SQL Server gateway)HS_FDS_CONNECT_INFO=sqlserver_dsnHS_FDS_TRACE_LEVEL=OFF
# Enable or disable specific capabilitiesHS_KEEP_REMOTE_COLUMN_SIZE=TRUEHS_NLS_NCHAR=UCS2HS_LANGUAGE=AMERICAN_AMERICA.AL32UTF8
# For ODBC generic connectivity — restrict what Oracle tries to pushHS_CALL_NAME=hs_call_nameRestart the gateway listener after modifying the init file.
7. For Oracle-to-Oracle Version Gaps: Avoid New Syntax in Remote Queries
Section titled “7. For Oracle-to-Oracle Version Gaps: Avoid New Syntax in Remote Queries”When the remote Oracle database is an older version, use only SQL syntax supported by that version:
-- Check remote Oracle versionSELECT banner FROM v$version@old_oracle_link;
-- Avoid 12c+ syntax when targeting 11g-- BAD for 11g remote: Uses FETCH FIRST (12c+)SELECT col1 FROM remote_table@old_oracle_linkFETCH FIRST 10 ROWS ONLY;
-- GOOD for 11g remote: Use ROWNUM (works on all Oracle versions)SELECT col1 FROM ( SELECT col1 FROM remote_table@old_oracle_link WHERE ROWNUM <= 10);Prevention Strategies
Section titled “Prevention Strategies”1. Document Gateway Capability Limitations at Project Start
Section titled “1. Document Gateway Capability Limitations at Project Start”Before building applications that use heterogeneous database links, query and document the capabilities of the target gateway:
-- Export all capabilities to a local table for referenceCREATE TABLE hs_capabilities_ref ASSELECT 'SQLSERVER_LINK' AS link_name, capability_name, capability_description, is_supportedFROM v$hs_capability@sqlserver_link;
-- Review unsupported capabilitiesSELECT capability_name, capability_descriptionFROM hs_capabilities_refWHERE is_supported = 'NO'ORDER BY capability_name;2. Isolate Remote Access Behind Local Packages
Section titled “2. Isolate Remote Access Behind Local Packages”Create a local PL/SQL package that abstracts all database link access. This package handles all translation and workarounds in one place:
CREATE OR REPLACE PACKAGE remote_data_access AS -- Instead of letting application code use raw DB links, -- expose clean APIs that handle ORA-02070 workarounds internally FUNCTION get_records_by_date(p_date IN DATE) RETURN SYS_REFCURSOR; PROCEDURE insert_record(p_id IN NUMBER, p_val IN VARCHAR2);END;/
CREATE OR REPLACE PACKAGE BODY remote_data_access AS FUNCTION get_records_by_date(p_date IN DATE) RETURN SYS_REFCURSOR AS v_rc SYS_REFCURSOR; v_start DATE := TRUNC(p_date); v_end DATE := TRUNC(p_date) + 1; BEGIN -- Bind pre-computed values to avoid function pushdown (prevents ORA-02070) OPEN v_rc FOR SELECT col1 FROM remote_table@sqlserver_link WHERE date_col >= v_start AND date_col < v_end; RETURN v_rc; END;END;/3. Use Bind Variables to Prevent Predicate Pushdown
Section titled “3. Use Bind Variables to Prevent Predicate Pushdown”Binding values rather than using expressions in WHERE clauses prevents Oracle from pushing Oracle-specific function calls to the remote database:
-- Instead of this (Oracle may push SYSDATE or TO_DATE to remote):SELECT * FROM remote_table@sqlserver_linkWHERE created_date > TO_DATE('2024-01-01', 'YYYY-MM-DD');
-- Use this (local variables are bound, not pushed):DECLARE v_cutoff DATE := DATE '2024-01-01';BEGIN -- v_cutoff is a resolved value; Oracle sends the date literal, not the function FOR r IN ( SELECT col1 FROM remote_table@sqlserver_link WHERE created_date > v_cutoff ) LOOP DBMS_OUTPUT.PUT_LINE(r.col1); END LOOP;END;4. Test All Queries Against the Heterogeneous Link in a Dev Environment First
Section titled “4. Test All Queries Against the Heterogeneous Link in a Dev Environment First”-- Create a standard test suite for all queries using a heterogeneous link-- Run this after any query change or Oracle patch applicationCREATE OR REPLACE PROCEDURE test_remote_link_queries ASBEGIN -- Test 1: Basic SELECT FOR r IN (SELECT 1 AS c1 FROM dual@sqlserver_link) LOOP DBMS_OUTPUT.PUT_LINE('Test 1 passed'); END LOOP;
-- Test 2: Filtered SELECT with bind variable DECLARE v_id NUMBER := 1; BEGIN FOR r IN (SELECT col1 FROM remote_table@sqlserver_link WHERE id = v_id) LOOP DBMS_OUTPUT.PUT_LINE('Test 2 passed: ' || r.col1); END LOOP; END;END;/5. Keep Oracle Gateways Updated
Section titled “5. Keep Oracle Gateways Updated”Ensure Oracle Database Gateway software is updated in line with the Oracle Database server version. Older gateway versions have fewer capabilities and produce more ORA-02070 errors:
-- Check gateway version in alert log or from the agent executable-- Compare with My Oracle Support for latest gateway patches for your remote database typeSELECT value FROM v$parameter WHERE name = 'diagnostic_dest';-- Review gateway agent trace files in the diagnostic destinationDiagnostic Scripts
Section titled “Diagnostic Scripts”These Oracle Day by Day utility scripts can assist with this type of investigation:
- gvsess.sql — Identify sessions using heterogeneous database links
Related Errors
Section titled “Related Errors”- ORA-02055 - Distributed update failed; rollback required
- ORA-02063 - Preceding line from database link (wraps errors from remote DB)
- ORA-02064 - Distributed operation not supported
- ORA-02067 - Transaction or savepoint rollback required
- ORA-02069 - GLOBAL_NAMES parameter must be TRUE
- ORA-02049 - Timeout: distributed transaction waiting for lock
Emergency Response
Section titled “Emergency Response”Quick Fixes
Section titled “Quick Fixes”-
Prevent Oracle from pushing the failing operation to the remote database
-- Wrap the remote query in an inline view with NO_MERGE hintSELECT /*+ NO_MERGE(r) */ *FROM (SELECT * FROM remote_table@failing_link) rWHERE TRUNC(r.date_col) = TRUNC(SYSDATE); -
Use passthrough SQL to bypass Oracle’s translation layer entirely
DECLAREv_cursor INTEGER;BEGINv_cursor := DBMS_HS_PASSTHROUGH.OPEN_CURSOR@failing_link;DBMS_HS_PASSTHROUGH.PARSE@failing_link(v_cursor, 'SELECT 1');DBMS_HS_PASSTHROUGH.CLOSE_CURSOR@failing_link(v_cursor);DBMS_OUTPUT.PUT_LINE('Passthrough connection confirmed working');END;/ -
Kill a runaway session issuing the failing query repeatedly
ALTER SYSTEM KILL SESSION 'sid,serial#' IMMEDIATE;
Post-Resolution Cleanup
Section titled “Post-Resolution Cleanup”-- Confirm the link is functional after applying the workaroundSELECT SYSDATE FROM dual@formerly_failing_link;
-- Document the capability limitation for future developersINSERT INTO hs_capabilities_ref (link_name, capability_name, is_supported)VALUES ('FAILING_LINK', 'TRUNC_FUNCTION', 'NO');COMMIT;
-- Verify no in-doubt transactions remain from failed attemptsSELECT COUNT(*) FROM dba_2pc_pending;
-- Review whether the gateway needs a patch or version upgradeSELECT value FROM v$parameter WHERE name = 'diagnostic_dest';-- Check gateway agent trace files at the diagnostic destination path