SKILL: CData CLI — ODBC Build
Build-phase companion to the cdata-cli skill. cdata-cli handles discovery
(connect, explore schema, validate SQL) using the JDBC driver; this skill takes over
once you start writing application code against the CData ODBC Driver — the
cross-language path for Node.js and any non-JVM runtime that speaks ODBC.
Everything validated during discovery carries straight over: all CData drivers share the
same relational model and SQL dialect, so table names, column names, and SQL confirmed via
cdatacli work unchanged through ODBC.
Language / Driver Edition
| Language | CData edition | ODBC binding |
|---|---|---|
| Node.js / JS / TS | CData ODBC Driver | odbc (npm) |
| PHP | CData ODBC Driver | PDO_ODBC (bundled PHP extension) |
| C / C++ | CData ODBC Driver | native ODBC API (sql.h, sqlext.h) |
| Go | CData ODBC Driver | database/sql + a community ODBC driver package |
| Ruby | CData ODBC Driver | a community ODBC gem |
| Rust | CData ODBC Driver | a community ODBC crate |
The ODBC driver is CData's; the binding that lets a language call ODBC is part of
that language's own ecosystem — not published by CData. Some are effectively standard
(Node's odbc, PHP's PDO_ODBC, the C/C++ ODBC API); others (Go, Ruby, Rust) rely on a
community package, since Go's database/sql, etc. provide only a generic database
interface with no built-in ODBC implementation. Confirm the current, maintained binding for
the target language rather than assuming a specific package. Only the Node odbc path
is shown as concrete example code below.
ODBC is the cross-language fallback. If a language has a first-class CData edition,
prefer it — Python → cdata-cli-python, .NET → cdata-cli-adonet; reach for ODBC
for languages that have no dedicated edition (Node.js, Go, PHP, Ruby, C/C++, Rust) or when
ODBC is specifically required.
Prerequisite: Discovery Done First
If cdata-cli has not been used yet in this session, stop and invoke it first. This skill only covers the build phase — connection setup, schema discovery, and SQL validation must be done through cdata-cli before continuing here.
This skill assumes cdata-cli has already been used to confirm the source connects and to
validate the exact tables/columns and SQL the app will run. Reuse those results here. If
discovery created a cdatacli connection that did an OAuth browser sign-in, note its
OAuthSettingsLocation — the app's DSN / connection string can point at the same cached
token file and skip re-authenticating (see Configure the Connection).
For runtime, the CLI works directly with Java/JDBC only. It cannot install, activate, or create connections for the ODBC Driver — that is what this skill covers. The ODBC Driver is a separate runtime driver edition installed as a system driver; the CLI has no role in obtaining or managing it.
Step 1: Confirm the ODBC Driver Is Installed
Unlike the JDBC jar (maven) or the ADO.NET provider (NuGet), the ODBC driver is a
system-installed driver registered with the OS ODBC Driver Manager. It is not fetched
from a package repo — it's installed from a CData ODBC setup package (installer/.msi on
Windows; distro package on Linux/macOS), and cdatacli has no role in installing it.
Check whether it's already present:
# Windows
Get-OdbcDriver | Where-Object Name -like "*CData*"
# e.g. "CData ODBC Driver for Salesforce" (note the 32-bit / 64-bit rows)
# Linux/macOS (unixODBC)
odbcinst -q -d # list registered driver names
The installed driver DLL/so typically lives at (Windows example):
C:\Program Files\CData\CData ODBC Driver for <Source>\lib64\CData.ODBC.<Source>.dll # 64-bit
Bitness must match. The app process, the ODBC Driver Manager, and the driver must all be the same architecture. A 64-bit Node/Go/PHP process needs the 64-bit CData ODBC driver and a DSN created in the 64-bit registry hive. Mixing 32/64-bit is the most common ODBC failure (see Troubleshooting).
If the driver isn't installed, the user must install the CData ODBC driver for the source first (from their CData download), then re-check.
Step 2: License
A working
cdatacliconnection doesn't mean this is activated.cdatacliruns on the JDBC driver, so it only proves JDBC is licensed — the ODBC driver is a separate license.
The CData ODBC driver is licensed as part of its installation (the setup wizard / CData license tool applies a trial or product key), and the license is per machine, separate from the JDBC/ADO.NET/Python editions. On a machine where the driver is already installed and licensed, no further action is needed.
If queries fail with a license error, the user should re-activate the ODBC driver through its installer / CData license tool (a product key is a secret — do this outside the AI coding session). Activation specifics are installer- and platform-driven, so defer to the CData ODBC driver's own setup rather than a scripted step here.
Step 3: Configure the Connection
First, check whether a DSN for this source already exists — if so, confirm with the user whether to reuse it:
# Windows — list all User and System DSNs
Get-OdbcDsn | Where-Object Name -like "*<Source>*"
# or list everything: Get-OdbcDsn
# Linux/macOS
odbcinst -q -s # list all registered DSN names
If a suitable DSN exists, skip to Step 4 and connect using DSN=<name>. Otherwise, set one up below.
Two ways to connect — a DSN (named, stored config) or DSN-less (full connection
string in code). Either way, reuse the properties validated in discovery, and for OAuth
sources point OAuthSettingsLocation at the discovery token cache so no secrets live in the
DSN/string and no browser flow is triggered.
Option A — DSN (recommended for reuse)
A User DSN needs no admin and is scoped to the current user; a System DSN is machine-wide and needs admin. Create a User DSN reusing the cached OAuth token:
# Windows — creates a 64-bit User DSN (no secrets stored: token comes from the cache file)
Add-OdbcDsn -Name "MySource SBX" -DriverName "CData ODBC Driver for <Source>" `
-DsnType User -Platform "64-bit" -SetPropertyValue @(
"AuthScheme=OAuth",
"InitiateOAuth=GETANDREFRESH",
"OAuthSettingsLocation=C:\...\CData\<Source> Data Provider\OAuthSettings-xxx.txt"
)
# Linux/macOS (unixODBC): register the driver in odbcinst.ini, then a DSN in odbc.ini
# ~/.odbc.ini
[MySource SBX]
Driver = CData ODBC Driver for <Source>
AuthScheme = OAuth
InitiateOAuth = GETANDREFRESH
OAuthSettingsLocation = /home/you/.cdata/OAuthSettings-xxx.txt
Connect with just the name: DSN=MySource SBX.
Option B — DSN-less (self-contained)
Put the driver name and all properties in the connection string — nothing to pre-register:
Driver={CData ODBC Driver for <Source>};AuthScheme=OAuth;InitiateOAuth=GETANDREFRESH;OAuthSettingsLocation=C:\...\OAuthSettings-xxx.txt
The Driver={...} value must be the exact driver name from Step 1.
Secrets: embedded-OAuth sources (e.g. Salesforce) need only the cached token. Sources with no embedded OAuth app (e.g. Gmail) require the user's own
OAuthClientId/OAuthClientSecreteven to refresh — supply those from environment variables at runtime, never in a checked-in DSN or string.
Step 4: Connect From Code
Node.js (odbc)
npm install odbc pulls a prebuilt binary — no C/C++ compiler needed on common
platforms.
import odbc from "odbc";
// DSN or DSN-less connection string both work:
const CS = process.env.SF_ODBC_DSN || "DSN=MySource SBX";
const pool = await odbc.pool(CS); // pool for a server app
const rows = await pool.query(
"SELECT Id, Name FROM Account WHERE Name LIKE ?", ["%Acme%"]); // ? placeholders
console.log(rows.length, rows[0]?.Name);
?placeholders (qmark), values passed as an array — always parameterize input.pool.queryfor reads; the same for INSERT/UPDATE/DELETE. Useodbc.connect(CS)for a single short-lived connection.
Other languages
The pattern is identical everywhere ODBC is available: connect with DSN=<name> (or a
DSN-less Driver={...} string), execute SQL with ? parameter markers, iterate results.
Use the target language's own ODBC binding (see the table above), and the same SQL you
validated with cdatacli.
Step 5: End-to-End Example (Node.js)
A minimal repository slice (wrap in Express/Fastify/etc. as needed):
import odbc from "odbc";
const CS = process.env.SF_ODBC_DSN || "DSN=MySource SBX";
let poolPromise = null;
const getPool = () => (poolPromise ??= odbc.pool(CS));
export async function listAccounts(search, limit = 50) {
const pool = await getPool();
let sql = "SELECT Id, Name FROM Account";
const params = [];
if (search) { sql += " WHERE Name LIKE ?"; params.push(`%${search}%`); }
sql += ` ORDER BY Name LIMIT ${Number(limit)}`;
return pool.query(sql, params);
}
export async function createAccount(name, industry = null) {
const pool = await getPool();
return pool.query("INSERT INTO Account (Name, Industry) VALUES (?, ?)", [name, industry]);
}
Troubleshooting
| Symptom | Cause / fix |
|---|---|
Data source name not found and no default driver specified / architecture mismatch |
Bitness mismatch, or DSN registered in the wrong hive. Ensure app process, Driver Manager, and driver are all 64-bit (or all 32-bit) and the DSN exists in the matching hive. |
| Driver name not found (DSN-less) | The Driver={...} name doesn't match an installed driver. Use the exact name from Get-OdbcDriver / odbcinst -q -d. |
| License error at query time | ODBC driver not licensed on this machine — re-activate via the CData ODBC installer / license tool (Step 2), outside the AI session. |
'OAuthClientId' and 'OAuthClientSecret' are needed to initiate OAuth |
Source has no embedded OAuth app; add client id/secret (from env vars) to the DSN/connection string, matching the app whose refresh token is cached. |
npm install odbc tries to compile / fails |
Usually a prebuilt binary is used; if compilation is triggered, ensure a supported Node version and platform, or install build tools for the fallback native build. |