Stop Escaping Quotes in Oracle SQL: Meet the q Operator


If we've spent any time writing SQL or PL/SQL in Oracle, we've run into the headache of single quotes inside text strings. Names like D'Souza, phrases like it's, or dynamic SQL with layers of nested strings all force us into the same clunky workaround: doubling up quotes ('Rahul''s book'). It works, but it's easy to miscount, hard to read, and a common source of syntax errors.

Oracle has a much cleaner answer: the Alternative Quoting Mechanism, better known as the q operator. Introduced in Oracle 10g, it lets us sidestep quote-escaping entirely by defining our own string delimiters.

Let's look at how to use it to keep SQL and PL/SQL clean and readable.

How It Works

The syntax is simple:

q'delimiter your_string delimiter'

We prefix the string with q, open with a single quote, choose a delimiter character, write the string as-is (apostrophes included), and close with the same delimiter followed by a single quote.

Oracle gives us two ways to pick a delimiter:

1. Paired bracket delimiters[ ], { }, ( ), < > We use an opening bracket, and Oracle automatically expects the matching closing bracket to end the string. This is usually the safest and most readable option.

2. Single-character delimiters — almost anything else Characters like !, #, |, or ^ work fine too, as long as that character doesn't appear in the string.

Using It in Standard SQL

Since the q operator is a native SQL feature, we can use it anywhere we'd normally write a string literal.

In an INSERT statement:

INSERT INTO books (title, author) 

VALUES (q'{Chetan's Guide to IIT}', 'Chetan Bhagat');


In a WHERE clause:


SELECT employee_id, first_name, last_name
FROM employees
WHERE last_name = q'<D'Souza>';


In a simple SELECT:

SELECT q'[It's a beautiful day in Bengaluru!]' AS greeting
FROM dual;

No doubled-up quotes anywhere — just the string, exactly as written.



Using It in PL/SQL

The operator works identically inside PL/SQL blocks, which is especially handy when we're assembling dynamic SQL or working with variables that hold quoted text.

sql
DECLARE
  v_name VARCHAR2(30) := q'!Khushee's laptop!';
BEGIN
  DBMS_OUTPUT.PUT_LINE('The first item is: ' || v_name);

  v_name := q'[D'Silva's car]';
  DBMS_OUTPUT.PUT_LINE('The second item is: ' || v_name);
END;
/

Output:

The first item is: Khushee's laptop
The second item is: D'Silva's car

Notice the two different delimiter styles in action: ! as a single-character delimiter in the first assignment, and [ ] as a paired bracket delimiter in the second. Both produce clean output with the apostrophe intact.

Why Make the Switch

  • Instant readability — our code is easier to scan, review, and maintain, especially in strings with multiple apostrophes.
  • Fewer bugs — no more counting single quotes to figure out where a string starts and ends.
  • A dynamic SQL lifesaver — invaluable when building complex dynamic SQL strings where quotes would otherwise nest several layers deep.

Next time we catch ourselves typing '''' just to escape a string, let's stop, reach for the q operator instead and let a custom delimiter do the work for us.


Fixing PPP Sequence Numbers in Oracle Payment Templates

 

The Problem

Oracle's Payment Process Profile (PPP) generates its built-in sequence numbers in steps of 2  like  2, 4, 6, 8, 10 …

For Our NACHA files we expect a clean, incrementing sequence like -  1, 2, 3, 4, 5 …

This post walks through the logic used to convert the PPP's "every-other-number" output into the correct simple sequence.

The Data, Visualized

Here's what the transformation looks like in practice. The Template Output column is what Oracle's PPP produces natively in our output, and Desired Value is the sequence we actually need:





The pattern is clear: every time the PPP's raw counter jumps by 2, our desired output should only increase by 1. So the fix boils down to dividing the running offset by 2 and padding it back into the standard 3-digit sequence format.

The very first row is a special case - there's no PPP value yet (LastValue is empty), so the desired output simply starts at 1. This is exactly what the DECODE(..., '', '001', ...) branch of the logic below handles.

The Logic Used

DECODE(
    /OutboundPaymentInstruction/PaymentInstructionInfo/PaymentSequence
        [SequenceName='US_NACHA_DAILY_SEQ']/LastValue,
    '',
    '001',
    LPAD(
        (SEQUENCE_NUMBER(US_NACHA_DAILY_SEQ)
            - (/OutboundPaymentInstruction/PaymentInstructionInfo/PaymentSequence
                [SequenceName='US_NACHA_DAILY_SEQ']/LastValue) div 2
        ),
        3,
        '0'
    )
)

Or

/OutboundPaymentInstruction/PaymentInstructionInfo/PaymentSequence
    [SequenceName='US_NACHA_DAILY_SEQ']/LastValue) div 2

Breaking It Down

1. DECODE(... , '', '001', ...) Checks whether LastValue for the US_NACHA_DAILY_SEQ sequence is empty. If it's the very first run (no prior value exists), the sequence starts fresh at 001.

2. SEQUENCE_NUMBER(US_NACHA_DAILY_SEQ) - LastValue If a prior value does exist, this calculates the difference between the current auto-generated sequence number and the last recorded value — effectively measuring how far the counter has advanced since the last run.

3. ... div 2 Because the PPP always advances in steps of 2, dividing that difference by 2 converts the raw counter movement back into a true incrementing count — 1, 2, 3, 4 … instead of 2, 4, 6, 8 …

4. LPAD(..., 3, '0') Finally, the result is zero-padded to 3 digits (e.g., 6 becomes 006), matching the format NACHA and other downstream systems expect.

Result

With this logic in place, the PPP's native 2-step counter is transparently converted into a clean, correctly-padded 1-step sequence  with no changes needed anywhere else in the payment process.

How to access Oracle ATP database using Claude Code in Visual Studio Code IDE

Claude's integration with Oracle Autonomous Transaction Processing (ATP) opens up powerful possibilities for database management and AI-assisted development. Rather than manually writing complex SQL queries, managing schemas, or analyzing database performance, we can now leverage Claude's natural language capabilities to interact with our ATP instance directl, all while keeping our credentials secure on our machine.

In this post, we will walk through the step-by-step process of connecting Claude Code (the VS Code extension) to Oracle ATP on macOS, enabling seamless database queries and interactions through the Model Context Protocol (MCP). By the end, we will have a fully functional AI-powered database management environment running entirely in VS Code.


Part 1: Install VS Code Extensions

Step 1.1: Open VS Code

Launch Visual Studio Code on your Mac.

Step 1.2: Open Extensions Marketplace

Press Cmd+Shift+X or click the Extensions icon on the left sidebar (it looks like 4 squares).

We see a search box at the top that says "Search Extensions in Marketplace".

Step 1.3: Install Claude Code Extension

  1. In the search box, type: claude

  2. You'll see Claude Code by Anthropic

  3. Click the Install button

  4. Wait for it to finish (shows "Installed" when done)






Step 1.4: Install SQL Developer Extension

  1. In the search box, type: Oracle sql developer
  2. You'll see Oracle SQL Developer by Oracle
  3. Click the Install button
  4. Wait for it to finish




Step 1.5: Restart VS Code

After both extensions install:

  1. Close VS Code completely (Cmd+Q)
  2. Reopen VS Code

You should now see:

  • Claude Code icon in the left sidebar (looks like a Claude logo)
  • SQL Developer icon in the left sidebar (looks like a database)



Now let's understand how these tools work together:



How the Architecture Works

  1. In VS Code → Type in Claude Code: "Show me all employees"
  2. Claude Code Extension → Sends our request to Claude Code CLI
  3. Claude Code CLI → Routes through MCP Protocol
  4. MCP Protocol → Translates our English request to SQL
  5. SQLcl MCP Server → Takes the SQL, looks up our saved connection
  6. Saved Credentials → Unlocks our Oracle wallet
  7. Oracle ATP → Executes the query, sends results back
  8. Results come back through all layers → Claude explains them to us in VS Code

Now let's install everything. Follow these steps in order.

Follow the below blog post for initial setup of Brew, Java and sqlcl. The Initial setup remains same.

How to Connect Claude Desktop App to Oracle ATP using MCP Server on MacOS


Or We can directly create connection in VS Code  using sql developer connection.




We created the above connection earlier using admin user.
We can create new connection with different user , test , save and connect with new connection.
Click on the Plus (+) icon and provide the details..


After providing the details click on test to test the connection.


Save and close.
We will see two connection now.



We can verify the sqlcl as below.
Right click on the connection and click on OpenSQLcl





We can test the simple sql.
Select * from dual;





Now we will install Claude code CLI.


 Install Claude Code CLI

Run below command in Mac Terminal or VS code Terminal.

curl -fsSL https://claude.ai/install.sh | bash




Run the below command to verify:

claude --version





Register SQLcl as MCP Server

Run below command in terminal:

claude mcp add --transport stdio --scope user sqlcl -- /opt/homebrew/bin/sqlcl -mcp




Let us verify the registration by running the below command:

claude mcp list

We should see like: sqlcl: /opt/homebrew/bin/sqlcl -mcp - ✔ Connected



Now let us test one by one again from basic:

Test SQLcl Connection

sqlcl /nolog


This is working fine, we got the SQL prompt.

Now check MCP Server

exit from the SQL prompt and run below command in terminal.

claude mcp list




Now let us test Claude Code with SQL
Run the below command from terminal:

claude

This should Start Claude Code


it will ask for permission, Enter to confirm · Esc to cancel.




Claude code is launched.

Now we will do a simple query:
Use the sqlcl MCP server to list my saved Oracle connections.

Claude should respond with our connection names.



Here is the result.



Now let us query some data or describe a table.

Connect to DevATP , describe the table EXT_OBJ_STORAGE_ZIP_FILES and also query the table for data.





Now let us test from the claude code UI mode as well:


We have successfully connected and access Oracle ATP database using Claude Code in VS Code.

Here are the checklist we need to ensure we have completed to successfully use the above setup.

  • VS Code with Claude Code extension installed
  • VS Code with SQL Developer extension installed
  • Homebrew installed
  • Java installed
  • SQLcl installed
  • Oracle Wallet downloaded and extracted and saved at a safe location and accessible.
  • ATP connection saved in SQLcl 
  • Claude Code CLI installed
  • SQLcl registered as MCP server
  • SQL Developer configured with wallet path




  • How to Connect Claude Desktop App to Oracle ATP using MCP Server on MacOS

     Claude's integration with Oracle Autonomous Transaction Processing (ATP) opens up powerful possibilities for database management and AI-assisted development.

    In this comprehensive guide, we'll walk through the step-by-step process of connecting Claude Desktop app to Oracle ATP on macOS, enabling seamless database queries and interactions.


    Prerequisites & Architecture Overview

    Before we begin, here's how the connection works:

    1. SQLcl (SQL Command Line) acts as the bridge between Claude and Oracle ATP
    2. Claude Desktop communicates with SQLcl through an MCP (Model Context Protocol) server
    3. Wallet files secure the database credentials

    Here is the Architecture how we can connect and it works.



    Now, let's build this step by step.

    Step 1: Install Homebrew

    First, check if Homebrew is already installed on your Mac:

    Open Mac Terminal and run:
    brew --version

    If it is present then it will show like below:

    If it says command not found:

    Then run below:


    /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"


    It asks for our  Mac password and takes a few minutes. Then read the "Next steps" it prints at the end , it tells us to add Homebrew to our  PATH, and skipping it is why brew stays "not found" afterward:


    echo >> ~/.zprofile echo 'eval "$(/opt/homebrew/bin/brew shellenv zsh)"' >> ~/.zprofile eval "$(/opt/homebrew/bin/brew shellenv zsh)"

    Verify the installation:

    brew --version


    Step 2: Install SQLcl

    SQLcl is the command-line tool that Claude will use to interact with your Oracle ATP database.

    Run:

    brew install --cask sqlcl

    verify that SQLcl is available:

    sqlcl -v

    If you get a Java error, install Java first:

    Run Below to install Java:

    brew install --cask temurin@21

    Then verify Java is installed:

    java -version


    Now
    Run the SQLcl version check again:
    sqlcl -v


    Confirm the binary path:

    ls -l /opt/homebrew/bin/sqlcl



    Step 3: Download and Configure Wallet

    The wallet file contains the encrypted credentials needed to connect to Oracle ATP.

    Download the Wallet

    1. Log into the OCI Console
    2. Navigate to Autonomous Database → Database connection → Download wallet
    3. Select Instance Wallet
    4. Set a wallet password (this is separate from your database user password)

    Once downloaded Move it somewhere permanent — SQLcl reads it on every connect, so ~/Downloads  folder is not a good place for credentials as by mistake sometimes we can delete as well.


    Run below commands one by one 

    mkdir -p ~/oracle/wallets 

    unzip ~/Downloads/Wallet_devatp.zip -d ~/oracle/wallets/devatp

    chmod 700 ~/oracle/wallets/devatp

    ls -la ~/oracle/wallets/devatp

    Step 4: Identify The Service Name

    Run the below command:

    grep -o '^[a-z0-9_]*' ~/oracle/wallets/devatp/tnsnames.ora | sort -u

    We will get devatp_low, devatp_medium, devatp_high, devatp_tp etc.

    Step 5: Save The Connection


    Launch SQLcl:
    Run the below command in terminal:
    sqlcl /nolog

    Now in the sql prompt run below, we have used admin user in example, we can use any other DB user as well.The connection will prompt for the database password and save it securely.

    conn -save atp_mcp -savepwd admin@devatp_low?TNS_ADMIN=/Users/khusheesumit/oracle/wallets/devatp

    Step 6: Verify the Connection

    This is the step that predicts whether Claude will work, so don't skip it:

    In the sql prompt run below:

    conn -name atp_mcp 

    Now run a test query:

    select sys_context('userenv','db_name') from dual; 

    If this returns your database name, you're good to go. Now test the MCP connection:

    Run below in Mac terminal not in sql prompt.

    env -i HOME="$HOME" /opt/homebrew/bin/sqlcl -mcp


    Silence with no prompt returning is success; it's waiting on stdin for MCP messages. Ctrl+C to exit. Any Java or classpath error here means you need an env block in the config, and you've found it before Claude muddies the diagnosis.

    If you see Java or classpath errors, you'll need to add an environment block to Claude's config file later.

    The ATP side setup is done. Now lets download and configure the Claude desktop.

    Step 7: Download and Configure Claude Desktop

    Download Claude Desktop

    Visit https://claude.com/download and download the macOS version.

    Install and Launch

    1. Open the DMG file
    2. Move Claude to your Applications folder
    3. Launch Claude and sign in with your Google or email account













    Step 8: Configure the MCP Server

    Now we need to edit the claude_desktop_config.json file to add MCP server so that claude and connect to our ATP database.

    Open the Claude Desktop config file:

    Run the below in terminal:
    open -e ~/Library/Application\ Support/Claude/claude_desktop_config.json

    Alternatively, use Claude's Settings UI:

    1. Click your profile → SettingsDeveloper
    As shown below









    As there are not MCP servers currently added  so the file would look like below:


    We need to add below line of code,Just after:

      "coworkUserFilesPath": "/Users/khusheesumit/Claude",


    Or anywhere with the correct syntax and comma.


    "mcpServers": { "sqlcl": { "command": "/opt/homebrew/bin/sqlcl", "args": ["-mcp"] } }



    Our updated file would look like below:


    { "coworkUserFilesPath": "/Users/khusheesumit/Claude", "mcpServers": { "sqlcl": { "command": "/opt/homebrew/bin/sqlcl", "args": ["-mcp"] } }, "preferences": { "launchPreviewPersistedWorkspaces": [], "launchPreviewSessionScopedSessions": [], "coworkScheduledTasksEnabled": true, "coworkHipaaRestricted": false, "ccdScheduledTasksEnabled": true, "sidebarMode": "chat", "bypassPermissionsGateByAccount": { "9faa76ab-2958-48f6-adcc-d7012a42fa5e": false }, "coworkWebSearchEnabled": true, "coworkModelAutoFallbackByAccount": { "9faa76ab-2958-48f6-adcc-d7012a42fa5e": true }, "remoteToolsDeviceName": "macbook-air-local", "epitaxyPrefs": { "dframe-group-scopes": {}, "dframe-local-slice": { "pinnedOrder": [], "homeProjectsPinnedOrder": [] }, "starred-local-code-sessions": [], "starred-session-groups": [], "starred-cowork-spaces": [], "ccd-sessions-filter": { "state": { "selectedProjects": [] }, "version": 0 }, "desktop-frame.paneStore.v1": { "state": { "extraPanesByMode": {}, "colWeightsByMode": {}, "rowSplit": 0.5, "draftNonce": 0 }, "version": 4 } } } }


    Make sure your JSON syntax is correct, with proper commas and braces.


    Restart Claude Desktop

    Quit Claude completely and reopen it. If there are no errors, it should launch smoothly.


    Step 9: Verify MCP Connection in Claude

    Once Claude restarts:

    1. Go to your profile → SettingsDeveloper
    2. You should see your sqlcl MCP server running





    Now let us check if we are able to connect and query something from out ATP database from Claude desktop.


    Step 10: Test Your First Database Query

    Let's make sure everything works end-to-end.

    1. Return to the Claude home screen
    2. Ask Claude to connect to your ATP database using your saved connection name: atp_mcp
    3. Claude will prompt you to approve connections—accept these requests

    Now as we remember our MCP server is sqlcl and connection anime is atp_mcp which we had saved earlier.

    Now let us query the ATP.


    use sqlcl mcp server and atp_mcp connection and provide me the tables from the atp which has obj in name





    It will prompt below to establish connection:







    Keep allowing for all the above requests.


    Here is the result.



    Now let us fine tune , I have APEX_USER Schema and it has only two tables with OBJ in name. As shown below from sql developer.


    So claude also should provide us the same list.

    We will ask like below:


    Can you share the table list under APEX_USER schema and has OBJ in name?


    PERFECT!

    Here is the result.





    We have Successfully connected and tested our Claude desktop app with our ATP database

    using MCP server.


    Next Steps

    In the next posts we will explore below points.

    • Claude Code and SQL Developer extension with VS Code:
      • Integrating Claude into our IDE and connecting to ATP from there
    • Windows Setup: How to do all this on a Windows machine