Skip to content
Homeβ€Ί Databaseβ€Ί ORA-00942: Table or View Does Not Exist – Full Troubleshooting

ORA-00942: Table or View Does Not Exist – Full Troubleshooting

Where developers are forged. Β· Structured learning Β· Free forever.
πŸ“ Part of: PL/SQL β†’ Topic 15 of 27
One of the most common root causes of ORA-06512.
πŸ§‘β€πŸ’» Beginner-friendly β€” no prior Database experience needed
In this tutorial, you'll learn
One of the most common root causes of ORA-06512.
  • ORA-00942 fires for both missing objects and missing privileges β€” the error message deliberately does not distinguish between them for security reasons
  • Public synonyms create invisible, database-wide dependencies β€” dropping one breaks every unqualified reference in every schema that resolves through it
  • Role-based grants do not work for definer's rights PL/SQL β€” use direct grants to the procedure owner or switch to AUTHID CURRENT_USER
✦ Plain-English analogy ✦ Real code with output ✦ Interview questions
⚑Quick Answer
  • ORA-00942 means the database cannot resolve the object name at the current scope
  • The object may exist but be inaccessible due to missing grants, wrong schema, or dropped synonyms
  • Unlike ORA-06550 (compile-time), ORA-00942 is a runtime error that crashes the executing SQL statement
  • It propagates as ORA-06512 through the PL/SQL call stack like any other unhandled exception
  • Production insight: roughly 40% of ORA-00942 incidents trace back to missing or broken synonyms, not missing tables
  • Biggest mistake: assuming the table was dropped when it actually exists in a different schema or the user simply lacks the grant to see it
Production IncidentPublic Synonym Dropped During Maintenance β€” 23 Procedures Failed SimultaneouslyA DBA dropped a public synonym during a routine schema cleanup, causing 23 procedures across three schemas to fail with ORA-00942 within minutes.
SymptomAt 02:47 AM, the monitoring system flagged a sudden spike in ORA-00942 errors. Within 5 minutes, 23 stored procedures across three different application schemas were failing. Every error stack pointed to the same unqualified table name: ORDERS. No deployment had been executed. No DDL change was visible in the application release log.
AssumptionThe on-call team assumed the ORDERS table had been dropped by mistake. They spent roughly 2 hours checking the recycle bin, running flashback queries against DBA_RECYCLEBIN, and evaluating RMAN restore options. The table was never dropped β€” it had been sitting in its owner schema the entire time.
Root causeA DBA ran a schema cleanup script during the maintenance window. The script included DROP PUBLIC SYNONYM orders. It was intended to remove stale synonyms for decommissioned tables, but the ORDERS synonym was still actively used by 23 procedures across three schemas. Every one of those procedures referenced ORDERS without schema qualification β€” they relied on the public synonym to resolve the bare name to OWNER.ORDERS. When the synonym was dropped, every unqualified reference to ORDERS raised ORA-00942 at runtime.
FixImmediate fix: recreated the public synonym with CREATE PUBLIC SYNONYM orders FOR owner.orders. All 23 procedures recovered without recompilation. Longer-term changes: 1. Added a pre-deployment gate that queries DBA_SYNONYMS cross-referenced against ALL_SOURCE to detect any public synonym still referenced by application objects. If a synonym slated for removal has active references, the gate blocks the drop and lists every affected object. 2. Refactored all 23 procedures to use the fully qualified name OWNER.ORDERS instead of the bare name. This eliminated the public synonym dependency for the most critical table in the system. 3. Documented a runbook rule: no public synonym may be dropped without running the impact-analysis procedure first.
Key Lesson
Public synonyms are invisible dependencies β€” dropping one breaks every unqualified reference across every schema in the databaseAlways query DBA_DEPENDENCIES and search ALL_SOURCE before dropping any database object β€” the dependency tree may span schemas you do not own or even know aboutFully qualified table names eliminate synonym dependency entirely β€” schema.table resolves without synonym lookupPre-deployment and pre-maintenance checks should verify that all referenced objects exist and remain accessible before any DDL is executed
Production Debug GuideFrom error message to root cause resolution
ORA-00942 on a table name that definitely exists→Confirm the table is in a different schema: SELECT owner, object_name FROM all_objects WHERE object_name = 'TABLE_NAME' AND object_type IN ('TABLE','VIEW'). If found in another schema, either add schema qualification to the SQL or create a synonym pointing to that schema.
ORA-00942 fires immediately after a deployment or schema change→Check whether a synonym was dropped or altered: SELECT synonym_name, table_owner, table_name FROM all_synonyms WHERE synonym_name = 'OBJECT_NAME'. Then check whether a grant was revoked: SELECT grantee, privilege FROM dba_tab_privs WHERE table_name = 'OBJECT_NAME'. Either of these changes can appear silently in a migration script.
ORA-00942 on a view that exists and is VALID→The view itself exists, but one of its underlying tables or views may be inaccessible to the querying user. Query DBA_VIEWS to extract the view text, then verify that the executing user holds SELECT on every base object referenced inside the view definition.
ORA-00942 fires in one schema but the identical SQL succeeds in another schema→The failing schema is missing a synonym or a direct grant that the working schema has. Compare synonyms: SELECT * FROM all_synonyms WHERE synonym_name = 'OBJECT' AND owner IN ('FAILING_SCHEMA','WORKING_SCHEMA','PUBLIC'). Compare grants: SELECT grantee, privilege FROM dba_tab_privs WHERE table_name = 'OBJECT' AND grantee IN ('FAILING_SCHEMA','WORKING_SCHEMA'). The delta between the two result sets is the root cause.
ORA-00942 in dynamic SQL (EXECUTE IMMEDIATE) but not in static SQL within the same procedure→Dynamic SQL executes under the privileges of the current user at runtime. If the enclosing procedure is compiled with AUTHID DEFINER, static SQL uses the owner's privileges but EXECUTE IMMEDIATE still resolves names using the session user's context. Verify that the session user has a direct grant (not just a role-based grant) on the target object, and ensure a synonym is visible to that user.
ORA-00942 appears intermittently — sometimes the same statement succeeds→Intermittent ORA-00942 usually points to a database link timeout, a synonym that resolves over a DB link to a remote database that is sometimes unreachable, or a connection pool that routes some sessions to a user that lacks the required grant. Check whether the synonym references a DB link: SELECT db_link FROM all_synonyms WHERE synonym_name = 'OBJECT'. If a DB link is involved, test connectivity with SELECT 1 FROM dual@dblink.

ORA-00942: table or view does not exist is raised at runtime when Oracle cannot resolve an object reference inside a SQL statement. The object name may be correct in the source code, but the database cannot find it in the current execution context.

The error is deceptively simple. The table exists. The name is spelled correctly. The code worked yesterday. Yet the statement fails. The cause is almost always a context change: a missing synonym, a revoked privilege, a schema migration, or a dropped public synonym after a cleanup script. Each root cause demands a different diagnostic path, and conflating them wastes hours.

ORA-00942 is one of the most common root causes of ORA-06512 in production PL/SQL systems. A procedure that references a table via an unqualified name or a synonym will compile without complaint but fail at runtime the moment the synonym is dropped or the privilege is revoked. The resulting error stack shows ORA-00942 as the originating error followed by one or more ORA-06512 entries tracing the call chain back to the top-level caller.

This guide walks through Oracle's name resolution order, the three major root cause categories β€” missing objects, missing synonyms, and missing privileges β€” and closes with automated prevention checks you can schedule today.

Name Resolution: How Oracle Finds Your Table

Oracle resolves object names through a deterministic sequence. Understanding this sequence is the single most important concept for diagnosing ORA-00942.

When Oracle encounters an unqualified table name β€” a name without a schema prefix β€” it searches in the following order: 1. It checks the current schema for a table or view with that name. 2. If no local object is found, it checks for a private synonym in the current schema. 3. If no private synonym is found, it checks for a public synonym. 4. If none of the above resolve, ORA-00942 is raised.

When Oracle encounters a qualified table name (schema.table), it bypasses synonym lookup entirely and goes directly to the specified schema. If the object does not exist in that schema, or the user lacks privilege to see it, ORA-00942 is raised.

The privilege check happens after name resolution succeeds. Oracle first resolves the name to a specific schema and object, then verifies that the executing user holds the required privilege (typically SELECT) on that resolved object. If the name resolves but the privilege is missing, Oracle still raises ORA-00942 β€” not ORA-01031 (insufficient privileges). This is by design: Oracle's security model prevents confirming the existence of objects to users who lack access. It is also the single most confusing aspect of this error, because the message says "does not exist" when the real problem is a missing grant.

In PL/SQL, the AUTHID clause determines whose privileges apply during execution. Definer's rights procedures (AUTHID DEFINER, the default) use the procedure owner's privileges and synonym context for name resolution. Invoker's rights procedures (AUTHID CURRENT_USER) use the calling user's privileges and synonym context. This distinction governs which synonyms are visible and which grants are effective at runtime.

io/thecodeforge/debug/name_resolution.sql Β· SQL
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
-- Demonstrate Oracle name resolution order
-- Run as user APP_USER querying the ORDERS table

-- Step 1: Check for a table or view in the current schema
SELECT object_name, object_type
FROM user_objects
WHERE object_name = 'ORDERS'
  AND object_type IN ('TABLE', 'VIEW');
-- If found: resolves to current_schema.ORDERS

-- Step 2: Check for a private synonym in the current schema
SELECT synonym_name, table_owner, table_name
FROM user_synonyms
WHERE synonym_name = 'ORDERS';
-- If found: resolves to table_owner.table_name

-- Step 3: Check for a public synonym
SELECT synonym_name, table_owner, table_name
FROM all_synonyms
WHERE synonym_name = 'ORDERS'
  AND owner = 'PUBLIC';
-- If found: resolves to table_owner.table_name

-- Step 4: Check across all accessible schemas
SELECT owner, object_name, object_type
FROM all_objects
WHERE object_name = 'ORDERS'
  AND object_type IN ('TABLE', 'VIEW')
ORDER BY owner;
-- Shows every schema that contains an ORDERS table/view

-- Step 5: Check privileges on the resolved object
SELECT grantor, grantee, privilege, grantable
FROM all_tab_privs
WHERE table_name = 'ORDERS'
ORDER BY grantee, privilege;
-- Shows who holds SELECT, INSERT, UPDATE, DELETE on ORDERS

-- Step 6: Trace the full resolution path in one query
SELECT
  'Local Object' AS resolution_type,
  object_name    AS name,
  SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA') || '.' || object_name AS resolves_to
FROM user_objects
WHERE object_name = 'ORDERS'
  AND object_type IN ('TABLE', 'VIEW')
UNION ALL
SELECT
  'Private Synonym',
  synonym_name,
  table_owner || '.' || table_name
FROM user_synonyms
WHERE synonym_name = 'ORDERS'
UNION ALL
SELECT
  'Public Synonym',
  synonym_name,
  table_owner || '.' || table_name
FROM all_synonyms
WHERE synonym_name = 'ORDERS'
  AND owner = 'PUBLIC';
-- The first row returned is the resolution Oracle would use
Mental Model
Name Resolution Order
Oracle resolves names in a fixed, deterministic order β€” understanding the order tells you exactly where to look when resolution fails.
  • Unqualified name (ORDERS): current schema object β†’ private synonym β†’ public synonym β†’ ORA-00942
  • Qualified name (OWNER.ORDERS): direct lookup in specified schema β†’ ORA-00942
  • Privilege check happens AFTER resolution β€” a missing grant raises ORA-00942, not ORA-01031
  • Definer's rights procedures (AUTHID DEFINER) use the owner's privileges and synonyms for resolution
  • Invoker's rights procedures (AUTHID CURRENT_USER) use the caller's privileges and synonyms for resolution
πŸ“Š Production Insight
Oracle raises ORA-00942 for both genuinely missing objects AND objects that exist but are invisible due to missing privileges.
The error message is identical in both cases β€” the diagnosis requires checking both paths.
Rule of thumb: when ORA-00942 appears on an object that you believe exists, verify privileges before assuming the object was dropped.
🎯 Key Takeaway
Oracle resolves unqualified names in a fixed order: current schema object, private synonym, public synonym.
Privilege failures raise ORA-00942, not ORA-01031 β€” the error message is intentionally misleading for security reasons.
Bottom line: if the table exists but ORA-00942 still fires, check grants β€” the object is there, you just cannot see it.

Synonym Failures: The Invisible Dependency

Synonyms are the most common cause of ORA-00942 in multi-schema Oracle environments. They create implicit, invisible dependencies that span schemas β€” and dropping a synonym can break every unqualified reference that depends on it.

Public synonyms are the most dangerous because they are database-wide. Every schema in the instance can resolve an unqualified name through a public synonym. A single DROP PUBLIC SYNONYM command can break procedures, views, materialized view queries, and triggers across dozens of schemas simultaneously. The dependency is invisible because DBA_DEPENDENCIES does not reliably track public synonym resolution by PL/SQL objects β€” the dependency graph shows the synonym target, not the synonym itself.

Private synonyms are safer because they are schema-scoped. Dropping a private synonym only affects objects within that schema. But private synonyms still create implicit dependencies that are easy to overlook during schema reorganizations.

Broken synonyms β€” synonyms whose target object has been dropped or renamed β€” raise ORA-00980 (synonym translation is no longer valid) rather than ORA-00942. However, if the synonym itself is dropped, the resolution chain never reaches the synonym and ORA-00942 is raised instead. This distinction matters during diagnosis: ORA-00980 means a synonym exists but points to nothing; ORA-00942 means no synonym was found at all.

The diagnostic query for synonym-related ORA-00942 checks three locations: the current schema for private synonyms, PUBLIC for public synonyms, and the target schema for the actual object. If the object exists in another schema but no synonym points to it, the fix is either creating the synonym or qualifying the table name in the source code.

The permanent fix is to use fully qualified table names (schema.table) for all critical references. This eliminates synonym dependency entirely. The trade-off is code verbosity and coupling to the schema name, but in production the reliability gain is overwhelming.

io/thecodeforge/debug/synonym_diagnostics.sql Β· SQL
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
-- Diagnostic 1: Find all public synonyms actively used by application objects
-- Run this BEFORE any synonym cleanup or decommission script
SELECT DISTINCT
  d.referenced_name  AS synonym_or_table_name,
  d.referenced_owner AS target_schema,
  d.owner            AS dependent_schema,
  d.name             AS dependent_object,
  d.type             AS dependent_type
FROM dba_dependencies d
WHERE d.referenced_type IN ('TABLE', 'VIEW')
  AND d.owner IN ('APP_SCHEMA1', 'APP_SCHEMA2', 'APP_SCHEMA3')
  AND d.referenced_name IN (
    SELECT synonym_name
    FROM dba_synonyms
    WHERE owner = 'PUBLIC'
  )
ORDER BY d.referenced_name, d.owner;

-- Diagnostic 2: Check if a specific synonym exists and where it points
SELECT
  owner          AS synonym_owner,
  synonym_name,
  table_owner    AS target_owner,
  table_name     AS target_object,
  db_link
FROM all_synonyms
WHERE synonym_name = 'ORDERS'
ORDER BY DECODE(owner, 'PUBLIC', 2, 1), owner;
-- Private synonyms appear first, public synonyms second

-- Diagnostic 3: Find every PL/SQL object that depends on a synonym
SELECT
  owner,
  name,
  type,
  referenced_name,
  referenced_type
FROM dba_dependencies
WHERE referenced_name = 'ORDERS'
  AND referenced_type IN ('SYNONYM', 'TABLE', 'VIEW')
ORDER BY owner, type, name;

-- Diagnostic 4: Recreate a dropped public synonym
CREATE OR REPLACE PUBLIC SYNONYM orders
FOR owner.orders;
-- All procedures that reference 'ORDERS' without qualification
-- will resolve again immediately β€” no recompilation required

-- Diagnostic 5: Find all unqualified table references in PL/SQL source
-- These references are vulnerable to synonym drops
SELECT
  owner,
  name,
  type,
  line,
  TRIM(text) AS source_line
FROM all_source
WHERE UPPER(text) LIKE '%FROM ORDERS%'
   OR UPPER(text) LIKE '%INTO ORDERS%'
   OR UPPER(text) LIKE '%UPDATE ORDERS%'
   OR UPPER(text) LIKE '%DELETE FROM ORDERS%'
ORDER BY owner, name, line;
-- Review results: any line that does NOT include 'OWNER.ORDERS'
-- is relying on a synonym and is vulnerable

-- Diagnostic 6: Verify the target object exists and is VALID
SELECT owner, object_name, object_type, status, last_ddl_time
FROM all_objects
WHERE object_name = 'ORDERS'
  AND object_type IN ('TABLE', 'VIEW', 'SYNONYM')
ORDER BY owner, object_type;
⚠ Public Synonym Drop Blast Radius
πŸ“Š Production Insight
Public synonyms create invisible dependencies that span every schema in the database.
Dropping one breaks every unqualified reference β€” and the blast radius is the entire instance.
Rule: search ALL_SOURCE for the synonym name across every application schema before dropping any public synonym.
🎯 Key Takeaway
Synonyms are the most common cause of ORA-00942 in multi-schema environments.
Public synonyms are global β€” dropping one can break every unqualified reference in every schema.
Bottom line: use fully qualified table names for critical references β€” eliminate synonym dependency permanently.

Privilege Failures: ORA-00942 Masking ORA-01031

Oracle raises ORA-00942 when the executing user lacks SELECT privilege on a table or view. The object exists, the name is correct, the synonym resolves β€” but the user cannot see the resolved object. This is the most confusing variant of ORA-00942 because the error message says "table or view does not exist" when the real problem is a missing grant.

The reason is Oracle's security model: confirming an object's existence to a user who lacks access is treated as an information disclosure vulnerability. If Oracle raised ORA-01031 (insufficient privileges), the user would learn that the object exists. Instead, Oracle returns the same generic ORA-00942 for both conditions, keeping the object's existence opaque.

The diagnostic path is straightforward once you know to check: first, verify the object exists by querying DBA_OBJECTS as a DBA user. If the object exists, query DBA_TAB_PRIVS and DBA_ROLE_PRIVS to check whether the executing user has the required privilege either directly or through a role.

For definer's rights PL/SQL (AUTHID DEFINER, the default), the procedure owner needs the direct grant β€” not the calling user. For invoker's rights PL/SQL (AUTHID CURRENT_USER), the calling user needs the grant. This distinction is critical because it determines which user's privileges Oracle evaluates at runtime.

Role-based grants introduce an additional subtlety. Roles are active during interactive SQL sessions and inside invoker's rights PL/SQL, but they are not active during definer's rights PL/SQL compilation or execution. If a table privilege is granted through a role, a definer's rights procedure cannot use it. The fix is a direct GRANT to the procedure owner β€” or switching the procedure to AUTHID CURRENT_USER.

This is one of the most frequently misdiagnosed ORA-00942 scenarios. Engineers see the error, confirm the table exists, confirm a grant exists through a role, and conclude that Oracle is buggy. The real problem is the definer's rights compilation model ignoring role-based grants.

io/thecodeforge/debug/privilege_diagnostics.sql Β· SQL
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
-- Diagnostic 1: Verify the object exists (run as DBA)
SELECT owner, object_name, object_type, status, last_ddl_time
FROM dba_objects
WHERE object_name = 'ORDERS'
  AND object_type IN ('TABLE', 'VIEW');
-- If rows returned: the object exists β€” problem is privilege or synonym
-- If no rows: the object was dropped or never created

-- Diagnostic 2: Check direct grants on the object
SELECT grantor, grantee, privilege, grantable
FROM dba_tab_privs
WHERE table_name = 'ORDERS'
ORDER BY grantee, privilege;
-- Look for the executing user or the procedure owner in the grantee column

-- Diagnostic 3: Check if the user can see the object via ALL_OBJECTS
-- Run this connected AS the application user, not as DBA
SELECT owner, object_name, object_type
FROM all_objects
WHERE object_name = 'ORDERS'
  AND object_type IN ('TABLE', 'VIEW');
-- No rows: the user cannot see the object (privilege or synonym issue)
-- Rows returned: the user can see it (problem is elsewhere)

-- Diagnostic 4: Check role-based grants
-- These work for direct SQL and invoker's rights PL/SQL
-- but NOT for definer's rights PL/SQL
SELECT
  r.role,
  rp.table_name,
  rp.privilege,
  rp.grantable
FROM dba_role_privs r
JOIN role_tab_privs rp ON rp.role = r.granted_role
WHERE r.grantee = 'APP_USER'
  AND rp.table_name = 'ORDERS'
ORDER BY r.role, rp.privilege;

-- Diagnostic 5: Check the AUTHID of the failing procedure
SELECT
  owner,
  object_name,
  authid
FROM all_procedures
WHERE object_name = 'GET_ORDERS'
  AND owner = 'APP_SCHEMA';
-- DEFINER  = owner's privileges apply (roles ignored)
-- CURRENT_USER = caller's privileges apply (roles active)

-- Fix 1: Direct grant to the schema owner (works for definer's rights)
GRANT SELECT ON owner.orders TO app_schema;

-- Fix 2: Direct grant to the calling user (works for invoker's rights)
GRANT SELECT ON owner.orders TO app_user;

-- Fix 3: Switch procedure to invoker's rights if role-based grants must be used
CREATE OR REPLACE PROCEDURE app_schema.get_orders(
  p_order_id IN NUMBER
)
  AUTHID CURRENT_USER  -- Caller's privileges, roles active
IS
  v_rec owner.orders%ROWTYPE;
BEGIN
  SELECT * INTO v_rec
  FROM owner.orders  -- Fully qualified for safety
  WHERE order_id = p_order_id;
END get_orders;
/
Mental Model
Privilege vs. Existence Confusion
Oracle raises the same error for missing objects and missing privileges β€” the fix depends entirely on which one it actually is.
  • ORA-00942 for a missing object: the table was dropped or never created β€” verify with DBA_OBJECTS
  • ORA-00942 for a missing privilege: the table exists but the user cannot see it β€” verify with DBA_TAB_PRIVS
  • Oracle deliberately does not distinguish between the two because revealing object existence to unauthorized users is a security risk
  • Diagnostic step one: query DBA_OBJECTS as DBA β€” if the object exists, the problem is privilege, not existence
  • Definer's rights PL/SQL ignores role-based grants β€” only direct grants to the procedure owner are effective
πŸ“Š Production Insight
ORA-00942 for a missing privilege looks byte-for-byte identical to ORA-00942 for a missing object.
Engineers routinely waste hours checking recycle bins and flashback queries when the real problem is a revoked or role-based grant.
Rule: verify object existence first via DBA_OBJECTS as a DBA user β€” if the object exists, pivot immediately to privilege analysis.
🎯 Key Takeaway
Oracle raises ORA-00942 for both missing objects and missing privileges β€” the error message deliberately does not distinguish between them.
Diagnostic step one: query DBA_OBJECTS as DBA to verify existence. If the object is there, the problem is privilege.
Bottom line: if the table exists but ORA-00942 fires, check grants and AUTHID β€” the object is there, you just cannot see it.

Schema Qualification: The Permanent Fix

Fully qualified table names (schema.table) eliminate three classes of ORA-00942 failures at once: synonym drops, cross-schema resolution failures, and privilege confusion caused by synonym chains. The name resolves directly to the target schema without any synonym lookup step.

The trade-off is code verbosity and coupling to the schema name. Every table reference includes the schema prefix, which means renaming a schema or migrating to a different environment with different schema names requires updating every reference. In practice, schema renames are rare β€” they happen maybe once in the lifetime of a system β€” and the reliability gain far outweighs the coupling cost in production.

The migration pattern is incremental. You do not need to rewrite every SQL statement in a single sprint. Start by qualifying references to the most critical tables β€” the ones whose unavailability would cause the most business damage. Use static analysis queries against ALL_SOURCE to find unqualified references and prioritize by table criticality and call frequency.

For dynamic SQL, schema qualification must be enforced at the query construction level. Define a configuration package or a constant map that associates logical table names with fully qualified physical names. This centralizes the schema dependency into a single compilation unit and makes migration to a different schema a one-line configuration change rather than a grep-and-replace across hundreds of files.

Views deserve special attention. A view that references an unqualified table name inherits the synonym dependency of the schema where the view is compiled. If that synonym is dropped, the view becomes INVALID and any query against it raises ORA-04063 (which often cascades into ORA-00942 in the caller). Always use fully qualified names inside view definitions.

io/thecodeforge/patterns/schema_qualification.sql Β· SQL
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
-- Pattern 1: Fully qualified names in PL/SQL
-- Eliminates synonym dependency entirely
CREATE OR REPLACE PROCEDURE app_schema.get_order(
  p_order_id IN NUMBER,
  p_order    OUT data_owner.orders%ROWTYPE
) IS
BEGIN
  SELECT *
  INTO p_order
  FROM data_owner.orders  -- Fully qualified: schema.table
  WHERE order_id = p_order_id;
  -- Works regardless of synonym state
EXCEPTION
  WHEN NO_DATA_FOUND THEN
    RAISE_APPLICATION_ERROR(-20010, 'Order not found: ' || p_order_id);
END get_order;
/

-- Pattern 2: Centralized table name constants for dynamic SQL
CREATE OR REPLACE PACKAGE app_schema.table_ref AS
  -- Single source of truth for all schema-qualified table names
  c_orders     CONSTANT VARCHAR2(128) := 'DATA_OWNER.ORDERS';
  c_customers  CONSTANT VARCHAR2(128) := 'DATA_OWNER.CUSTOMERS';
  c_payments   CONSTANT VARCHAR2(128) := 'DATA_OWNER.PAYMENTS';
  c_audit_log  CONSTANT VARCHAR2(128) := 'DATA_OWNER.AUDIT_LOG';
  -- To migrate schemas, change the constants here and recompile
END table_ref;
/

-- Pattern 3: Using the constant in dynamic SQL
CREATE OR REPLACE PROCEDURE app_schema.count_by_status(
  p_status IN VARCHAR2,
  p_count  OUT NUMBER
) IS
  v_sql VARCHAR2(4000);
BEGIN
  v_sql := 'SELECT COUNT(*) FROM '
    || app_schema.table_ref.c_orders
    || ' WHERE status = :b_status';

  EXECUTE IMMEDIATE v_sql INTO p_count USING p_status;
END count_by_status;
/

-- Pattern 4: Views with fully qualified names
CREATE OR REPLACE VIEW app_schema.active_orders_v AS
SELECT
  o.order_id,
  o.customer_id,
  o.amount,
  o.status,
  c.customer_name
FROM data_owner.orders o       -- Fully qualified
JOIN data_owner.customers c    -- Fully qualified
  ON c.customer_id = o.customer_id
WHERE o.status = 'ACTIVE';
-- This view remains VALID regardless of synonym state
/

-- Pattern 5: Find all unqualified table references in a schema
-- Use this to plan an incremental migration to qualified names
SELECT
  owner,
  name,
  type,
  line,
  TRIM(text) AS source_line
FROM all_source
WHERE owner = 'APP_SCHEMA'
  AND type IN ('PACKAGE BODY', 'PROCEDURE', 'FUNCTION', 'TRIGGER')
  AND (
       REGEXP_LIKE(UPPER(text), '\bFROM\s+[A-Z_][A-Z0-9_]*\b')
    OR REGEXP_LIKE(UPPER(text), '\bJOIN\s+[A-Z_][A-Z0-9_]*\b')
    OR REGEXP_LIKE(UPPER(text), '\bINTO\s+[A-Z_][A-Z0-9_]*\b')
    OR REGEXP_LIKE(UPPER(text), '\bUPDATE\s+[A-Z_][A-Z0-9_]*\b')
    OR REGEXP_LIKE(UPPER(text), '\bDELETE\s+FROM\s+[A-Z_][A-Z0-9_]*\b')
  )
  AND NOT REGEXP_LIKE(UPPER(text), '\b[A-Z_][A-Z0-9_]*\.[A-Z_][A-Z0-9_]*\b')
ORDER BY owner, name, line;
-- Each row is an unqualified reference that depends on a synonym
πŸ’‘Schema Qualification Migration Strategy
  • Start with the most critical tables β€” qualify references to tables whose unavailability causes the most business impact
  • Use the static analysis query (Pattern 5) to find all unqualified references β€” prioritize by table criticality and call frequency
  • Centralize table name constants in a configuration package β€” makes schema migration a single recompile
  • Always use qualified names inside view definitions β€” an unqualified reference makes the view dependent on a synonym
  • Migrate incrementally: qualify one table at a time, test, deploy, repeat β€” do not attempt a full rewrite in a single change
πŸ“Š Production Insight
Unqualified table references create invisible synonym dependencies that break silently when a synonym is dropped.
Fully qualified names eliminate the dependency β€” the reference resolves regardless of synonym state.
Rule: qualify all critical table references in production code β€” the reliability gain far outweighs the code verbosity cost.
🎯 Key Takeaway
Schema qualification eliminates three classes of ORA-00942: synonym drops, cross-schema resolution failures, and privilege confusion from synonym chains.
The trade-off is code verbosity and schema coupling β€” but schema renames are vanishingly rare in production.
Bottom line: if your critical tables are referenced without schema qualification, you are one DROP SYNONYM away from a production incident.

Dynamic SQL and ORA-00942

Dynamic SQL introduces additional ORA-00942 risk because the object name is constructed at runtime as a string. The PL/SQL compiler cannot validate the reference at compile time β€” it is just a VARCHAR2 that may or may not resolve correctly when Oracle executes it.

The most common dynamic SQL ORA-00942 is caused by incorrect string concatenation. A missing space between FROM and the table name, an extra single quote, a wrong case when the table was created with double-quoted mixed-case identifiers β€” any of these produce a syntactically valid SQL string that references a non-existent object. The error message shows the constructed SQL, not the PL/SQL source line, which makes debugging painful if you did not log the string.

Another common cause is privilege context. EXECUTE IMMEDIATE uses the current session user's privileges, even when the enclosing procedure is compiled with AUTHID DEFINER. This is a subtle but critical distinction from static SQL. If the procedure owner has SELECT on a table but the calling user does not, static SQL in the same procedure succeeds while the dynamic SQL raises ORA-00942. The fix is either granting the privilege directly to the caller, using fully qualified names with a direct grant, or restructuring the dynamic SQL into a static cursor.

The third cause is synonym visibility. If the dynamic SQL uses an unqualified table name and the session user does not have a private synonym, and the public synonym was dropped, ORA-00942 is raised. The dynamic SQL is resolving names using the session user's synonym context, not the procedure owner's.

The non-negotiable practice for dynamic SQL is to log the fully constructed SQL string before execution. Without the actual string, ORA-00942 in dynamic SQL is a black box. DBMS_OUTPUT works for development; a dedicated logging table or the application's structured logging framework works for production.

io/thecodeforge/debug/dynamic_sql_ora00942.sql Β· SQL
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
-- WRONG: Dynamic SQL with unqualified name and no logging
CREATE OR REPLACE PROCEDURE app_schema.broken_dynamic(
  p_status IN VARCHAR2
) IS
  v_sql   VARCHAR2(4000);
  v_count NUMBER;
BEGIN
  -- Problem 1: unqualified name β€” depends on synonym
  -- Problem 2: no logging β€” impossible to debug
  -- Problem 3: string concatenation for value β€” SQL injection risk
  v_sql := 'SELECT COUNT(*) FROM ORDERS WHERE status = ''' || p_status || '''';
  EXECUTE IMMEDIATE v_sql INTO v_count;
END broken_dynamic;
/

-- CORRECT: Dynamic SQL with qualified name, logging, and bind variables
CREATE OR REPLACE PROCEDURE app_schema.safe_dynamic(
  p_status IN VARCHAR2
) IS
  v_sql   VARCHAR2(4000);
  v_count NUMBER;
BEGIN
  -- Fully qualified name β€” no synonym dependency
  -- Bind variable β€” no SQL injection, no quoting errors
  v_sql := 'SELECT COUNT(*) FROM data_owner.orders WHERE status = :b_status';

  -- Log the SQL before execution β€” critical for ORA-00942 diagnosis
  DBMS_OUTPUT.PUT_LINE('Executing: ' || v_sql);

  EXECUTE IMMEDIATE v_sql INTO v_count USING p_status;

  DBMS_OUTPUT.PUT_LINE('Result: ' || v_count);
EXCEPTION
  WHEN OTHERS THEN
    -- Log the failed SQL with the error β€” the only way to diagnose runtime failures
    DBMS_OUTPUT.PUT_LINE('FAILED SQL: ' || v_sql);
    DBMS_OUTPUT.PUT_LINE('ERROR: ' || SQLERRM);
    RAISE;  -- Re-raise after logging
END safe_dynamic;
/

-- CORRECT: Dynamic SQL with privilege context awareness
CREATE OR REPLACE PROCEDURE app_schema.privilege_aware_dynamic
  AUTHID CURRENT_USER  -- Uses caller's privileges
IS
  v_sql   VARCHAR2(4000);
  v_count NUMBER;
BEGIN
  -- AUTHID CURRENT_USER: EXECUTE IMMEDIATE uses the CALLER's privileges
  -- If the caller lacks SELECT on data_owner.orders: ORA-00942
  v_sql := 'SELECT COUNT(*) FROM data_owner.orders WHERE status = :b_status';

  BEGIN
    EXECUTE IMMEDIATE v_sql INTO v_count USING 'ACTIVE';
    DBMS_OUTPUT.PUT_LINE('Count: ' || v_count);
  EXCEPTION
    WHEN OTHERS THEN
      RAISE_APPLICATION_ERROR(
        -20001,
        'Dynamic SQL failed. '
        || 'SQL: ' || v_sql
        || ' | Error: ' || SQLERRM
        || ' | Session User: ' || SYS_CONTEXT('USERENV', 'SESSION_USER')
        || ' | Note: AUTHID CURRENT_USER means caller privileges apply',
        TRUE  -- Preserve the original error stack
      );
  END;
END privilege_aware_dynamic;
/
⚠ Dynamic SQL ORA-00942 Pitfalls
πŸ“Š Production Insight
Dynamic SQL ORA-00942 is effectively impossible to diagnose without logging the constructed SQL string.
The error stack shows the EXECUTE IMMEDIATE line number, not the SQL content β€” if you did not log it, you are debugging blind.
Rule: always log the SQL string before EXECUTE IMMEDIATE β€” it is the only reliable way to diagnose runtime name resolution failures.
🎯 Key Takeaway
Dynamic SQL introduces runtime name resolution that bypasses the compiler's validation entirely.
Log the constructed SQL before every EXECUTE IMMEDIATE β€” without it, ORA-00942 is a black box.
Bottom line: if your dynamic SQL does not log the constructed string before execution, you will spend hours reproducing what could have been diagnosed in seconds.

Preventing ORA-00942: Automated Dependency Checks

ORA-00942 prevention requires automated checks that run at two points in the lifecycle: before deployments and on a recurring schedule. The checks verify that all referenced objects exist, are accessible, have valid synonyms, and have the required grants in place.

The pre-deployment check queries DBA_DEPENDENCIES for every object in the deployment changeset and verifies that every referenced object exists and has a VALID status. If any reference is broken β€” a missing table, an INVALID synonym, a dropped view β€” the deployment is blocked with a list of missing or invalid objects. This catches problems before they reach production.

The daily health check scans all application schemas for INVALID objects, broken synonyms (synonyms whose target has been dropped), and missing grants. It runs on a schedule and alerts the DBA team on any anomaly. The goal is to detect silent degradation β€” a revoked grant, a dropped synonym, a quietly invalidated package β€” before it triggers a runtime ORA-00942 during business hours.

The synonym impact check runs before any DROP SYNONYM command. It searches ALL_SOURCE for references to the synonym name across every application schema and reports the blast radius: how many objects in how many schemas reference the synonym. If the count is greater than zero, the drop is blocked until every reference is either updated to use a qualified name or a replacement synonym is created.

Combined, these three checks convert ORA-00942 from a runtime surprise into a pre-detected, preventable issue. The implementation cost is a handful of stored procedures and a scheduler job. The benefit is eliminating an entire class of production outages.

io/thecodeforge/monitor/ora00942_prevention.sql Β· SQL
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
-- Check 1: Pre-deployment dependency validation
-- Run before every deployment to verify all references resolve
CREATE OR REPLACE PROCEDURE app_schema.check_dependencies(
  p_schema IN VARCHAR2
) IS
  v_missing_count NUMBER := 0;
BEGIN
  FOR rec IN (
    SELECT
      d.owner,
      d.name,
      d.type,
      d.referenced_name,
      d.referenced_type,
      d.referenced_owner
    FROM dba_dependencies d
    WHERE d.owner = UPPER(p_schema)
      AND d.referenced_owner NOT IN ('SYS', 'SYSTEM', 'PUBLIC')
      AND NOT EXISTS (
        SELECT 1
        FROM dba_objects o
        WHERE o.owner       = d.referenced_owner
          AND o.object_name = d.referenced_name
          AND o.object_type = d.referenced_type
          AND o.status      = 'VALID'
      )
    ORDER BY d.name, d.referenced_name
  ) LOOP
    v_missing_count := v_missing_count + 1;
    DBMS_OUTPUT.PUT_LINE(
      'BROKEN REF: ' || rec.type || ' ' || rec.owner || '.' || rec.name
      || ' -> ' || rec.referenced_type
      || ' ' || rec.referenced_owner || '.' || rec.referenced_name
    );
  END LOOP;

  IF v_missing_count > 0 THEN
    RAISE_APPLICATION_ERROR(
      -20070,
      'Deployment blocked: ' || v_missing_count
      || ' broken object references in schema ' || p_schema
    );
  ELSE
    DBMS_OUTPUT.PUT_LINE('All dependencies valid in schema ' || p_schema);
  END IF;
END check_dependencies;
/

-- Check 2: Daily synonym integrity check
-- Detects synonyms that point to non-existent or invalid objects
CREATE OR REPLACE PROCEDURE app_schema.check_synonyms IS
  v_broken_count NUMBER := 0;
BEGIN
  FOR rec IN (
    SELECT
      s.owner        AS synonym_owner,
      s.synonym_name,
      s.table_owner  AS target_owner,
      s.table_name   AS target_object
    FROM dba_synonyms s
    WHERE s.owner IN ('PUBLIC', 'APP_SCHEMA', 'APP_SCHEMA2', 'APP_SCHEMA3')
      AND s.db_link IS NULL  -- Skip DB link synonyms for this check
      AND NOT EXISTS (
        SELECT 1
        FROM dba_objects o
        WHERE o.owner       = s.table_owner
          AND o.object_name = s.table_name
          AND o.status      = 'VALID'
      )
    ORDER BY s.owner, s.synonym_name
  ) LOOP
    v_broken_count := v_broken_count + 1;
    DBMS_OUTPUT.PUT_LINE(
      'BROKEN SYNONYM: ' || rec.synonym_owner || '.' || rec.synonym_name
      || ' -> ' || rec.target_owner || '.' || rec.target_object || ' (missing or invalid)'
    );
  END LOOP;

  IF v_broken_count > 0 THEN
    DBMS_OUTPUT.PUT_LINE(v_broken_count || ' broken synonym(s) detected β€” review and fix before business hours');
  ELSE
    DBMS_OUTPUT.PUT_LINE('All synonyms valid');
  END IF;
END check_synonyms;
/

-- Check 3: Synonym drop impact analysis
-- Run BEFORE any DROP SYNONYM command to measure blast radius
CREATE OR REPLACE PROCEDURE app_schema.analyze_synonym_drop(
  p_synonym_name IN VARCHAR2
) IS
  v_reference_count NUMBER := 0;
BEGIN
  -- Count source code references across all application schemas
  SELECT COUNT(*) INTO v_reference_count
  FROM all_source
  WHERE REGEXP_LIKE(UPPER(text), '\b' || UPPER(p_synonym_name) || '\b')
    AND owner NOT IN ('SYS', 'SYSTEM')
    AND type IN ('PACKAGE BODY', 'PROCEDURE', 'FUNCTION', 'TRIGGER', 'VIEW');

  IF v_reference_count > 0 THEN
    DBMS_OUTPUT.PUT_LINE(
      'WARNING: ' || v_reference_count
      || ' source references found for synonym ' || UPPER(p_synonym_name)
    );
    DBMS_OUTPUT.PUT_LINE(
      'Dropping this synonym WILL cause ORA-00942 in the following objects:'
    );

    FOR rec IN (
      SELECT DISTINCT owner, name, type
      FROM all_source
      WHERE REGEXP_LIKE(UPPER(text), '\b' || UPPER(p_synonym_name) || '\b')
        AND owner NOT IN ('SYS', 'SYSTEM')
        AND type IN ('PACKAGE BODY', 'PROCEDURE', 'FUNCTION', 'TRIGGER', 'VIEW')
      ORDER BY owner, type, name
    ) LOOP
      DBMS_OUTPUT.PUT_LINE('  ' || rec.type || ' ' || rec.owner || '.' || rec.name);
    END LOOP;

    RAISE_APPLICATION_ERROR(
      -20072,
      'Drop blocked: ' || v_reference_count
      || ' objects reference synonym ' || UPPER(p_synonym_name)
      || '. Update references to qualified names before dropping.'
    );
  ELSE
    DBMS_OUTPUT.PUT_LINE(
      'No references found for synonym ' || UPPER(p_synonym_name) || '. Safe to drop.'
    );
  END IF;
END analyze_synonym_drop;
/

-- Schedule the daily checks using DBMS_SCHEDULER
BEGIN
  DBMS_SCHEDULER.CREATE_JOB(
    job_name        => 'APP_SCHEMA.DAILY_SYNONYM_CHECK',
    job_type        => 'PLSQL_BLOCK',
    job_action      => 'BEGIN app_schema.check_synonyms; END;',
    start_date      => SYSTIMESTAMP,
    repeat_interval => 'FREQ=DAILY; BYHOUR=6; BYMINUTE=0',
    enabled         => TRUE,
    comments        => 'Daily check for broken synonyms β€” prevents ORA-00942 at runtime'
  );
END;
/
πŸ’‘ORA-00942 Prevention Checklist
  • Pre-deployment: run check_dependencies to verify all referenced objects exist and are VALID
  • Daily at 06:00: run check_synonyms to detect broken synonyms before business hours
  • Before DROP SYNONYM: run analyze_synonym_drop to measure blast radius and block if references exist
  • Weekly: check for INVALID objects: SELECT owner, object_name, object_type FROM dba_objects WHERE status = 'INVALID' AND owner IN (application schemas)
  • Use fully qualified names for the top 20 most critical tables β€” eliminates synonym dependency where it matters most
πŸ“Š Production Insight
ORA-00942 that appears 'suddenly' in production was building silently β€” a synonym was dropped, a grant was revoked, or an object was invalidated during off-hours maintenance.
Automated dependency and synonym checks detect the problem hours or days before it manifests as a runtime failure.
Rule: run dependency validation before every deployment and synonym integrity checks daily. Prevention is cheaper than incident response.
🎯 Key Takeaway
Automated checks convert ORA-00942 from a runtime surprise into a detected and preventable issue.
Three checks cover the full surface area: pre-deployment dependency validation, daily synonym integrity, and synonym drop impact analysis.
Bottom line: if your only detection method for ORA-00942 is runtime errors in production, you are detecting far too late β€” add preventive checks today.
πŸ—‚ ORA-00942 Root Causes and Fixes
Diagnostic path for each common cause
Root CauseDiagnostic QueryFixPrevention
Table or view genuinely droppedSELECT * FROM dba_objects WHERE object_name = 'NAME' AND object_type IN ('TABLE','VIEW')Recreate the table or restore from backup / flashbackPre-deployment dependency validation; daily INVALID object checks
Missing private or public synonymSELECT * FROM all_synonyms WHERE synonym_name = 'NAME'CREATE [PUBLIC] SYNONYM name FOR schema.nameDaily synonym integrity check; synonym drop impact analysis
Missing SELECT grant (appears as ORA-00942, not ORA-01031)SELECT * FROM dba_tab_privs WHERE table_name = 'NAME' AND grantee = 'USER'GRANT SELECT ON schema.table TO userDaily grant verification; document required grants per schema
Wrong schema β€” object exists elsewhereSELECT owner FROM all_objects WHERE object_name = 'NAME' AND object_type IN ('TABLE','VIEW')Add schema qualification: schema.table or create a synonymUse fully qualified names in all source code
Public synonym dropped during cleanupSELECT * FROM dba_synonyms WHERE synonym_name = 'NAME' AND owner = 'PUBLIC'Recreate: CREATE PUBLIC SYNONYM name FOR schema.nameRun analyze_synonym_drop before every DROP SYNONYM
Role-based grant in definer's rights procedureCheck AUTHID of the procedure via ALL_PROCEDURESIssue a direct GRANT to the procedure owner or switch to AUTHID CURRENT_USERUse direct grants for all definer's rights procedures; document the pattern

🎯 Key Takeaways

  • ORA-00942 fires for both missing objects and missing privileges β€” the error message deliberately does not distinguish between them for security reasons
  • Public synonyms create invisible, database-wide dependencies β€” dropping one breaks every unqualified reference in every schema that resolves through it
  • Role-based grants do not work for definer's rights PL/SQL β€” use direct grants to the procedure owner or switch to AUTHID CURRENT_USER
  • Fully qualified table names (schema.table) eliminate synonym dependency permanently β€” the reference resolves without any synonym lookup
  • Dynamic SQL ORA-00942 is impossible to diagnose without logging the constructed SQL string before execution
  • Automated dependency and synonym integrity checks convert ORA-00942 from a runtime surprise into a detected and preventable issue

⚠ Common Mistakes to Avoid

    βœ•Assuming the table was dropped when ORA-00942 fires
    Symptom

    Engineers check the recycle bin, flashback queries, and RMAN backup restore options for a table that was never dropped. Hours are wasted when the real problem is a missing synonym or a revoked grant.

    Fix

    First, query DBA_OBJECTS as a DBA user to verify the object exists. If it exists, check DBA_TAB_PRIVS for grants and ALL_SYNONYMS for synonyms. The object is almost certainly there β€” the problem is accessibility, not existence.

    βœ•Dropping public synonyms without checking references across all schemas
    Symptom

    A public synonym is dropped during a schema cleanup or decommission. Within minutes, procedures across multiple schemas begin failing with ORA-00942. The blast radius is the entire database because every schema could have been using that synonym.

    Fix

    Before dropping any public synonym, run the analyze_synonym_drop procedure to search ALL_SOURCE for references across every application schema. If references exist, update them to use fully qualified names before dropping the synonym. Never drop first and fix later.

    βœ•Using role-based grants for definer's rights PL/SQL and expecting them to work
    Symptom

    The grant exists through a role, the user can SELECT the table in SQL*Plus, but the definer's rights procedure still raises ORA-00942. Engineers conclude Oracle is broken when the real issue is that roles are not active during definer's rights execution.

    Fix

    Issue a direct GRANT to the procedure owner schema: GRANT SELECT ON schema.table TO owner_schema. Alternatively, switch the procedure to AUTHID CURRENT_USER (invoker's rights), which activates roles at runtime. Document which model each procedure uses.

    βœ•Using unqualified table names in dynamic SQL without logging the constructed string
    Symptom

    Dynamic SQL raises ORA-00942 but the error stack shows only the EXECUTE IMMEDIATE line number, not the SQL content. Without logging the SQL string before execution, the engineer cannot determine which table reference failed, what the actual string looked like, or why resolution failed.

    Fix

    Always log the fully constructed SQL string before EXECUTE IMMEDIATE β€” to DBMS_OUTPUT during development and to a logging table in production. Use fully qualified table names in the string literal. Use bind variables for all values to prevent injection and quoting errors.

    βœ•Not checking dependencies before deploying schema changes (table renames, schema reorganizations, object drops)
    Symptom

    A table rename or schema reorganization breaks procedures, views, materialized views, and triggers that reference the old name. The deployment script reports success because the DDL succeeded, but runtime ORA-00942 failures appear minutes later when application traffic hits the changed objects.

    Fix

    Run check_dependencies before every deployment to verify all referenced objects exist and are VALID. Query DBA_DEPENDENCIES for every object in the deployment changeset. If any reference cannot be resolved, block the deployment and fix the dependency chain first.

    βœ•Confusing ORA-00942 with ORA-00980 and applying the wrong fix
    Symptom

    ORA-00980 (synonym translation is no longer valid) is misdiagnosed as ORA-00942, or vice versa. The engineer creates a new synonym when the existing synonym is broken, or drops and recreates the synonym when the underlying table is the actual missing object.

    Fix

    ORA-00980 means a synonym exists but its target is gone β€” fix the target or drop the stale synonym. ORA-00942 means no synonym or object was found at all β€” create the synonym or qualify the name. Check ALL_SYNONYMS first to determine which error you are actually dealing with.

Interview Questions on This Topic

  • QWhat causes ORA-00942 and how do you diagnose it?JuniorReveal
    ORA-00942 is raised when Oracle cannot resolve an object name at runtime. The three most common causes are: the object does not exist (it was dropped or never created), the object exists in a different schema without a synonym or schema qualification, or the executing user lacks SELECT privilege on the object. The diagnostic sequence is: first, query DBA_OBJECTS as a DBA to verify the object exists. If it exists, check DBA_TAB_PRIVS for grants and ALL_SYNONYMS for synonyms. If the object exists and grants are in place, check whether the procedure uses AUTHID DEFINER (needs direct grant to owner) or AUTHID CURRENT_USER (can use role-based grants). The error is the same for all three causes, so the diagnosis must proceed by elimination.
  • QWhy does ORA-00942 fire for missing privileges instead of ORA-01031?Mid-levelReveal
    Oracle raises ORA-00942 for both missing objects and missing privileges because revealing the existence of an object to an unauthorized user is classified as an information disclosure vulnerability. If Oracle raised ORA-01031 (insufficient privileges), the user would learn that the object exists even though they cannot access it. By returning the same generic ORA-00942, Oracle prevents this information leak. The practical diagnostic step is to query DBA_OBJECTS as a DBA user β€” if the object exists, the problem is privilege, not existence. This behavior is by design and is documented in the Oracle Security Guide.
  • QHow do public synonyms create invisible dependencies, and what is the blast radius of dropping one?Mid-levelReveal
    Public synonyms are visible to every schema in the database. When a PL/SQL procedure references a table using an unqualified name, it relies on the public synonym resolution step to map the bare name to schema.table. The dependency is invisible because DBA_DEPENDENCIES records the resolved table as the dependency target, not the synonym that was used to resolve it. Dropping a public synonym breaks every unqualified reference across every schema that was resolving through it. The blast radius is the entire database instance. The safe approach is to search ALL_SOURCE across all application schemas for the synonym name before dropping, qualify all critical references with schema.table to eliminate the dependency, and use a gate procedure that blocks the drop if active references exist.
  • QWhy do role-based grants fail for definer's rights PL/SQL, and what are the alternatives?SeniorReveal
    Definer's rights procedures (AUTHID DEFINER, the default) are compiled and executed using the owner's directly granted privileges. Oracle deliberately disables roles during definer's rights compilation and execution because roles can be enabled or disabled at the session level, which would make compilation results non-deterministic. If a table privilege is granted only through a role, the definer's rights procedure cannot resolve the reference and raises ORA-00942 at runtime. There are two fixes: issue a direct GRANT SELECT ON schema.table TO procedure_owner, which makes the privilege visible during definer's rights execution; or change the procedure to AUTHID CURRENT_USER (invoker's rights), which uses the caller's privileges at runtime where roles are active. The trade-off with invoker's rights is that every caller must have the required grants, whereas definer's rights centralizes the privilege in the procedure owner.
  • QHow would you design a prevention system that eliminates ORA-00942 from a production environment?SeniorReveal
    A three-layer prevention system covers the full surface area. First, mandate fully qualified table names (schema.table) for all critical object references in PL/SQL and views β€” this eliminates synonym dependency at the source level. Second, implement pre-deployment dependency checks that query DBA_DEPENDENCIES for every object in the deployment changeset and block the deployment if any referenced object is missing or INVALID. Third, schedule daily synonym and grant integrity checks that scan all application schemas for broken synonyms and missing grants, alerting the DBA team before a runtime failure occurs. Additionally, implement a synonym drop gate procedure that blocks DROP SYNONYM commands if ALL_SOURCE contains active references to the synonym. The cost is a few stored procedures and a scheduler job; the benefit is converting an entire class of runtime errors into pre-detected, preventable issues.

Frequently Asked Questions

What is the difference between ORA-00942 and ORA-00980?

ORA-00942 means the table or view does not exist or is not accessible β€” no synonym or object could be found for the given name. ORA-00980 means a synonym exists but its target object has been dropped, renamed, or is otherwise no longer valid β€” the synonym translation is broken. ORA-00980 specifically indicates a stale synonym, while ORA-00942 is a broader error covering missing objects, missing synonyms, and missing grants. The fix for ORA-00980 is to repair or drop the broken synonym; the fix for ORA-00942 depends on which of the three root causes applies.

How do I find which synonym is causing ORA-00942?

Query ALL_SYNONYMS for the object name: SELECT owner, synonym_name, table_owner, table_name FROM all_synonyms WHERE synonym_name = 'OBJECT_NAME' ORDER BY DECODE(owner, 'PUBLIC', 2, 1). Check both private synonyms (visible in USER_SYNONYMS) and public synonyms (where owner = 'PUBLIC'). If a synonym exists, verify its target object is present and VALID: SELECT owner, object_name, status FROM dba_objects WHERE owner = synonym's table_owner AND object_name = synonym's table_name. If no synonym exists at all, that is the cause β€” create one or qualify the table name.

Can ORA-00942 occur on a view that exists and is VALID?

Yes. A view can exist and show VALID status, but querying it raises ORA-00942 if its underlying base table is inaccessible to the querying user. The view compiles under the view owner's privileges, but if the querying user lacks SELECT on the view itself, or if the view's base tables have been dropped since the view was last compiled, ORA-00942 is raised. Query DBA_VIEWS to extract the view text, then check privileges on every base table and view referenced inside the definition.

How do I check if a specific user has SELECT privilege on a specific table?

Check direct grants: SELECT grantor, privilege, grantable FROM dba_tab_privs WHERE table_name = 'TABLE_NAME' AND grantee = 'USER_NAME'. Also check role-based grants: SELECT rp.role, rp.privilege FROM role_tab_privs rp JOIN dba_role_privs drp ON drp.granted_role = rp.role WHERE drp.grantee = 'USER_NAME' AND rp.table_name = 'TABLE_NAME'. Remember that role-based grants work for direct SQL and invoker's rights PL/SQL but do not work for definer's rights PL/SQL compilation or execution.

Should I use synonyms or fully qualified names in production code?

Use fully qualified names (schema.table) for critical production references. Synonyms add a layer of indirection that creates invisible dependencies, complicates troubleshooting, and introduces a failure mode (synonym drop) that qualified names are immune to. The reliability gain of qualified names outweighs the code verbosity cost. Reserve synonyms for specific use cases: cross-database links where the remote schema name may vary by environment, or when the schema name genuinely must be configurable at deployment time without recompilation. For everything else, qualify the name and eliminate the dependency.

Can ORA-00942 appear in a SELECT on a table I own in my own schema?

Yes, in rare cases. If you created the table using double-quoted mixed-case identifiers (e.g., CREATE TABLE "Orders" ...), the object name is stored in mixed case. An unquoted reference like SELECT FROM Orders resolves to the uppercase name ORDERS, which does not match the mixed-case name "Orders". The fix is to use the exact case with double quotes: SELECT FROM "Orders". This is uncommon but catches people who create tables from ORM tools that double-quote identifiers by default.

πŸ”₯
Naren Founder & Author

Developer and founder of TheCodeForge. I built this site because I was tired of tutorials that explain what to type without explaining why it works. Every article here is written to make concepts actually click.

← PreviousORA-06550: PL/SQL Compilation Error – The Complete GuideNext β†’ORA-01017: Invalid Username/Password – Common Causes & Solutions
Forged with πŸ”₯ at TheCodeForge.io β€” Where Developers Are Forged