diff --git a/README.md b/README.md index c7a05f1..8a4d747 100644 --- a/README.md +++ b/README.md @@ -8,16 +8,29 @@ The Skills follow the [vercel-labs/skills](https://github.com/vercel-labs/skills | Skill | Description | |---|---| -| [`cdata-cli`](skills/cdata-cli/SKILL.md) | Connect, query, explore schema, run SQL, search/download/activate CData JDBC drivers, and generate source-specific skills via the `cdatacli` tool. | +| [`cdata-cli`](skills/cdata-cli/SKILL.md) | **Discovery** — connect, query, explore schema, run SQL, download/activate JDBC drivers, and generate source-specific skills via `cdatacli`. Foundation for the build skills below. | +| [`cdata-cli-python`](skills/cdata-cli-python/SKILL.md) | **Build** Python apps with the CData Python Connector (DB-API 2.0). | +| [`cdata-cli-adonet`](skills/cdata-cli-adonet/SKILL.md) | **Build** C# / .NET apps with the CData ADO.NET Data Provider (NuGet). | +| [`cdata-cli-odbc`](skills/cdata-cli-odbc/SKILL.md) | **Build** Node.js / non-JVM apps with the CData ODBC Driver. | +| [`cdata-cli-java`](skills/cdata-cli-java/SKILL.md) | **Build** Java apps with the CData JDBC Driver. | ## Install -Install the skills: +Install all skills: ```bash npx skills add CDataSoftware/cli-skills ``` +Or install individual skills by name: + +```bash +npx skills add CDataSoftware/cli-skills --skill cdata-cli +npx skills add CDataSoftware/cli-skills --skill cdata-cli-python +``` + +The build skills assume `cdata-cli` for discovery — install it alongside whichever build skill(s) you need. + ## Prerequisites The `cdata-cli` skill drives the `cdatacli` executable — a Java CLI (requires Java 17+) for CData JDBC drivers. After installing the CLI, `cdatacli` is on `PATH` and discovers drivers from `./` or `./lib/` relative to the executable. diff --git a/skills/cdata-cli-adonet/SKILL.md b/skills/cdata-cli-adonet/SKILL.md new file mode 100644 index 0000000..4af0dd5 --- /dev/null +++ b/skills/cdata-cli-adonet/SKILL.md @@ -0,0 +1,254 @@ +--- +name: cdata-cli-adonet +description: "Use when writing C# / .NET application code (also VB.NET, F#) that connects to a data source through a CData driver — after the connection and SQL have been validated with the cdata-cli discovery skill. Covers mapping the language to the CData ADO.NET Data Provider edition, adding the provider from NuGet (CData.), activating its license, and connecting via the ADO.NET classes (Connection / Command). This is the .NET build-phase companion to cdata-cli; invoke it whenever the target application runs on .NET." +license: MIT +metadata: + author: CData Software + version: "1.0" +--- + +# SKILL: CData CLI — .NET / ADO.NET 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 .NET application code** using the **CData ADO.NET Data Provider**. + +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 the ADO.NET provider. + +--- + +## Language / Driver Edition + +| Language | CData edition | .NET namespace | +|---|---|---| +| **C#** (also VB.NET, F#) | **CData ADO.NET Data Provider** | `System.Data.CData.` | + +The provider follows standard ADO.NET naming — for `` (e.g. `Salesforce`): + +- `Connection`, `Command`, `DataReader`, `DataAdapter`, + `Parameter`, `CommandBuilder`, `ConnectionStringBuilder` +- Assembly/DLL: `System.Data.CData..dll` + +--- + +## Prerequisite: Discovery Done First + +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 .NET app can point at the same cached token file and skip +re-authenticating (see **Connect**). + +> The CLI's `drivers download` is JDBC-only and cannot fetch the ADO.NET provider. It comes +> from NuGet (below). + +--- + +## Step 1: Get the Provider (NuGet) + +The ADO.NET provider is published on NuGet by **CDataSoftware** as **`CData.`** +(e.g. `CData.Salesforce`). The idiomatic install is a package reference in the project: + +```bash +dotnet add package CData. +``` + +- Grab the plain **`CData.`** package — that's the ADO.NET provider. The + `CData..EntityFrameworkCore*` packages are **EF Core add-ons**; only add those if + you're using Entity Framework. +- To pin a version: `dotnet add package CData. --version `. + +**Already installed locally?** The standalone ADO.NET provider *product* is a **separate +distribution** from the NuGet package and may expose a **different set of target folders**. +If it's installed on the machine, you can reference its DLL directly instead of pulling +from NuGet: + +``` +C:\Program Files\CData\CData ADO.NET Provider for \lib\\System.Data.CData..dll +``` + +where `` is one of the folders present under `lib\` (e.g. `net8.0`, `net6.0`, +`netstandard2.0`). + +--- + +## Step 2: Reference It & Pick a Target Framework + +The NuGet package ships its build targets under `lib/`. The set can vary by connector and +version, so **check the package's `lib/` folder**; for the connector verified here it was +`netstandard2.0` and `net40`: + +| Target in package | Use for | +|---|---| +| `netstandard2.0` | .NET Core / .NET 5+ (net6.0, net8.0, net10.0, …) — **needs license activation (Step 3)** | +| `net40` | .NET Framework (4.x) — **self-licenses on NuGet install** | + +**Pick the `TargetFramework` from the installed SDK, not the runtime list.** `dotnet new +--framework` and the build only accept TFMs the installed **SDK** knows — a box with only +the .NET 10 SDK rejects `net6.0`/`net8.0` even though those *runtimes* exist. Check +`dotnet --version` (or `dotnet --list-sdks`) and default to the SDK's matching TFM (e.g. +.NET 10 SDK → `net10.0`, or `net10.0-windows` for a WinForms/WPF UI); since an SDK ships +its own runtime, that target both builds and runs. + +Only if you deliberately target an **older** TFM do you also need that TFM's runtime +installed (`dotnet --list-runtimes`) — otherwise launch fails with *"You must install or +update .NET to run this application."* The `netstandard2.0` provider itself works across +net6.0 / net8.0 / net10.0, so the limiting factor is your toolchain, not the driver. + +Two ways to reference the provider: + +- **PackageReference** (from `dotnet add package`) — simplest; NuGet restores the DLL. +- **Direct `HintPath`** — reference the extracted `netstandard2.0\System.Data.CData..dll`. + Useful when you want the DLL (and its activated `.lic`) in a known local path: + +```xml + + + ..\packages\CData.\lib\netstandard2.0\System.Data.CData..dll + + +``` + +--- + +## Step 3: Activate the License + +> **A working `cdatacli` connection doesn't mean this is activated.** `cdatacli` runs on the +> JDBC driver, so it only proves *JDBC* is licensed — the ADO.NET provider is a separate license. + +Licensing depends on the build target and is **per machine**, separate from the +JDBC/Python/ODBC editions: + +- **.NET Framework (`net40`)** — the NuGet package **auto-installs a license on restore**; + no action needed. +- **.NET Core / .NET (`netstandard2.0`)** — **must be activated** with the `install-license` + tool bundled in the package's `tools/` folder, or the first query raises *"Could not find + a valid license…"*. + +The tool is a .NET app. Its folder holds `install-license.dll`, `install-license.runtimeconfig.json`, +and `prod.inf`; find it under the package: + +``` +\packages\CData.\tools\install-license.dll # extracted package +%USERPROFILE%\.nuget\packages\cdata.\\tools\install-license.dll # global NuGet cache +``` + +It reads `prod.inf` from the current directory, so **run it from its own folder**. Because +it prompts for input, **have the user run it in a normal terminal — outside the AI coding +session** (an AI session can't reliably answer the prompts, and it hangs if run with no +arguments). + +**Trial:** run it and answer the `Name:` / `Email:` prompts: + +```powershell +cd "\packages\CData.\tools" +dotnet .\install-license.dll +``` + +**Purchased key** (the key is a secret — also keep this outside an AI session): + +```powershell +cd "\packages\CData.\tools" +dotnet .\install-license.dll YOUR-PRODUCT-KEY +``` + +Activation writes `System.Data.CData..lic` next to the provider DLL +(`lib\netstandard2.0\`). If you reference the DLL by `HintPath`, keep the `.lic` beside +that copy. + +--- + +## Step 4: Connect (ADO.NET) + +Standard ADO.NET usage — and unlike some other editions, these objects **do** implement +`IDisposable`, so `using` blocks are the right pattern: + +```csharp +using System.Data.CData.Salesforce; + +using var conn = new SalesforceConnection(connectionString); +conn.Open(); + +using var cmd = new SalesforceCommand( + "SELECT Id, Name FROM Account WHERE Name LIKE @name", conn); +cmd.Parameters.Add(new SalesforceParameter("@name", "%Acme%")); + +using var reader = cmd.ExecuteReader(); +while (reader.Read()) + Console.WriteLine(reader["Name"]); +``` + +- **Named parameters** — placeholders are `@name` and you add `Parameter` objects. + Always parameterize user input. +- `ExecuteNonQuery()` for INSERT/UPDATE/DELETE; `ExecuteScalar()` for single values; + `DataAdapter` + `DataTable` to fill a grid. + +### Connection string & auth (reuse discovery, keep secrets out of code) + +Pass the same properties validated with `cdatacli`, from config (e.g. `appsettings.json`), +not hard-coded. For **OAuth** sources, point `OAuthSettingsLocation` at the token file the +discovery connection already populated so the app refreshes silently, no browser flow: + +```text +AuthScheme=OAuth;InitiateOAuth=GETANDREFRESH;UseSandbox=true;OAuthSettingsLocation=C:\...\OAuthSettings-xxx.txt +``` + +Sources with **no embedded OAuth app** require the user's own `OAuthClientId`/ +`OAuthClientSecret` even to refresh — read those from environment variables / user-secrets, +never commit them. + +--- + +## Step 5: End-to-End Example + +A minimal console/repository slice (wrap in WinForms/WPF/ASP.NET as needed): + +```csharp +using System.Data; +using System.Data.CData.Salesforce; + +const string CS = + @"AuthScheme=OAuth;InitiateOAuth=GETANDREFRESH;UseSandbox=true;OAuthSettingsLocation=C:\...\OAuthSettings-sbx.txt"; + +static IEnumerable<(string Id, string Name)> ListAccounts(string? search, int limit = 50) +{ + using var conn = new SalesforceConnection(CS); + conn.Open(); + var sql = "SELECT Id, Name FROM Account" + + (string.IsNullOrWhiteSpace(search) ? "" : " WHERE Name LIKE @s") + + $" ORDER BY Name LIMIT {limit}"; + using var cmd = new SalesforceCommand(sql, conn); + if (!string.IsNullOrWhiteSpace(search)) + cmd.Parameters.Add(new SalesforceParameter("@s", $"%{search}%")); + using var r = cmd.ExecuteReader(); + while (r.Read()) + yield return (r["Id"].ToString()!, r["Name"].ToString()!); +} + +static void CreateAccount(string name, string? industry) +{ + using var conn = new SalesforceConnection(CS); + conn.Open(); + using var cmd = new SalesforceCommand( + "INSERT INTO Account (Name, Industry) VALUES (@n, @i)", conn); + cmd.Parameters.Add(new SalesforceParameter("@n", name)); + cmd.Parameters.Add(new SalesforceParameter("@i", (object?)industry ?? DBNull.Value)); + cmd.ExecuteNonQuery(); +} +``` + +Build to confirm the reference resolves: `dotnet build`. + +--- + +## Troubleshooting + +| Error | Cause / fix | +|---|---| +| `Could not find a valid license for using CData ADO.NET Provider for ` | The `netstandard2.0` build isn't activated on this machine — run `install-license` from the package `tools/` folder (Step 3). `net40` self-licenses, so this only affects .NET Core / .NET targets. | +| `You must install or update .NET to run this application` | The target TFM's **runtime** isn't installed. Simplest: target the SDK's own version (Step 2), which ships a matching runtime; otherwise install the missing runtime. | +| `'OAuthClientId' and 'OAuthClientSecret' are needed to initiate OAuth` | Source has no embedded OAuth app; supply client id/secret in the connection string (from env vars / user-secrets), matching the app whose refresh token is cached. | +| Reference/`using System.Data.CData.` not found | Package not restored or wrong package. Ensure `dotnet add package CData.` (the plain provider, not an `EntityFrameworkCore` variant) and `dotnet restore`. | +| `install-license.dll` hangs or errors on `prod.inf` | Run it **from the `tools/` folder** with `Name`/`Email` (trial) or a product-key argument; don't run it with no arguments. | diff --git a/skills/cdata-cli-java/SKILL.md b/skills/cdata-cli-java/SKILL.md new file mode 100644 index 0000000..44c2b9d --- /dev/null +++ b/skills/cdata-cli-java/SKILL.md @@ -0,0 +1,215 @@ +--- +name: cdata-cli-java +description: "Use when writing Java (or other JVM language — Kotlin, Scala) application code that connects to a data source through a CData driver, after the connection and SQL have been validated with the cdata-cli discovery skill. Covers the CData JDBC Driver edition: reusing the same jar cdata-cli already downloaded and activated, putting it on the classpath (or adding it as a Maven/Gradle dependency), and connecting via JDBC (DriverManager, the jdbc:: URL, PreparedStatement). This is the JVM build-phase companion to cdata-cli." +license: MIT +metadata: + author: CData Software + version: "1.0" +--- + +# SKILL: CData CLI — Java / JDBC 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 JVM application code** using that same **CData JDBC Driver**. + +This is the most continuous of the build skills: the JDBC jar an app needs is **exactly the +jar `cdata-cli` already downloaded and activated** during discovery. Everything validated +there — tables, columns, SQL — runs unchanged through JDBC. + +--- + +## Language / Driver Edition + +| Language | CData edition | Entry points | +|---|---|---| +| **Java** (also Kotlin, Scala, other JVM) | **CData JDBC Driver** | Driver class `cdata.jdbc..Driver`; URL `jdbc::` | + +`` is the lowercased driver name (e.g. `salesforce`) and `` its cased form +(e.g. `Salesforce`): jar `cdata.jdbc.salesforce.jar`, class +`cdata.jdbc.salesforce.SalesforceDriver`, URL prefix `jdbc:salesforce:`. + +--- + +## Prerequisite: Discovery Done First + +This skill assumes `cdata-cli` has already been used to confirm the source connects and to +validate the tables/columns and SQL the app will run. Because the CLI runs on JDBC, two +things are usually **already done** for you: + +- **The jar is already downloaded** — `cdatacli drivers download` placed + `cdata.jdbc..jar` in `./lib/` (or you know the installed-product path). +- **The jar is likely already licensed** — if discovery ran `cdatacli drivers activate`, + `drivers list` shows `"activated": true` and the same jar is ready for the app. + +If discovery created an **OAuth** connection, note its `OAuthSettingsLocation` — the app's +JDBC URL can point at the same cached token file and skip re-authenticating (see +**Connect**). + +--- + +## Step 1: Get the Driver Jar + +Prefer the jar you already have from discovery; otherwise pick one of the alternatives. + +**A. Reuse the CLI jar (simplest).** It's the exact driver, already local: + +``` +./lib/cdata.jdbc..jar # where cdatacli drivers download put it +``` + +**B. Installed-product path** (if the JDBC driver product is installed): + +``` +C:\Program Files\CData\CData JDBC Driver for \lib\cdata.jdbc..jar # Windows +/Applications/CData/CData JDBC Driver for /lib/cdata.jdbc..jar # macOS +``` + +**C. Maven/Gradle dependency** from CData's JDBC maven repo. Coordinates follow the CLI +catalog URL — repo base `https://maven.cdata.com/p/jdbc/`, groupId `cdata`, artifactId +`-jdbc`. Confirm the exact artifactId/version with `cdatacli drivers search --driver `: + +```kotlin +// Gradle (Kotlin DSL) — verify coordinates against `cdatacli drivers search` output +repositories { maven { url = uri("https://maven.cdata.com/p/jdbc/") } } +dependencies { implementation("cdata:salesforce-jdbc:26.0.9666") } +``` + +```xml + +cdatahttps://maven.cdata.com/p/jdbc/ + + cdatasalesforce-jdbc26.0.9666 + +``` + +If a build can't resolve the remote repo, fall back to installing the local jar into your +build (Gradle `implementation(files("libs/cdata.jdbc..jar"))`, or Maven +`mvn install:install-file`). + +--- + +## Step 2: License + +The JDBC driver is **licensed per machine**, separate from the ADO.NET/ODBC/Python +editions. If discovery already activated the jar you're using, the app needs nothing more. + +To activate (if needed): + +- **Via the CLI** (trial is safe in-session; a purchased key is a secret — run it outside + the AI session): + ```bash + cdatacli drivers activate --name "First Last" --email you@example.com --trial + cdatacli drivers activate --name "First Last" --email you@example.com --key XXXXX + ``` +- **Via the jar's GUI wizard:** double-click / `java -jar cdata.jdbc..jar`. + +The driver looks for its license next to the jar (`cdata.jdbc..lic`) or in the +user's CData folder (`~/.CData/cdata.jdbc..lic`, i.e. +`%USERPROFILE%\.CData\` on Windows). Keep the `.lic` with the jar you ship, or ensure the +runtime user has it in `~/.CData/`. + +--- + +## Step 3: Put It on the Classpath + +> **OS note:** classpath separator is `;` on Windows (PowerShell/cmd), `:` on macOS/Linux. +> Relative paths resolve against the current directory — `cd` into the source folder or use +> absolute paths. + +```bash +javac -cp "cdata.jdbc..jar" App.java +java -cp ".:cdata.jdbc..jar" App # macOS/Linux — Windows: ".;cdata.jdbc..jar" +``` + +With Maven/Gradle, the dependency from Step 1 puts it on the classpath automatically. Modern +JDBC (4.0+) **auto-registers** the driver via `ServiceLoader`, so `Class.forName(...)` is not +required — the jar just needs to be on the classpath. + +--- + +## Step 4: Connect (JDBC) + +JDBC objects are `AutoCloseable`, so use try-with-resources: + +```java +import java.sql.*; + +String url = "jdbc:salesforce:AuthScheme=OAuth;InitiateOAuth=GETANDREFRESH;UseSandbox=true;" + + "OAuthSettingsLocation=C:\\...\\OAuthSettings-sbx.txt"; + +try (Connection conn = DriverManager.getConnection(url); + PreparedStatement ps = conn.prepareStatement( + "SELECT Id, Name FROM Account WHERE Name LIKE ?")) { + ps.setString(1, "%Acme%"); // ? positional params + try (ResultSet rs = ps.executeQuery()) { + while (rs.next()) System.out.println(rs.getString("Name")); + } +} +``` + +- The **JDBC URL** is `jdbc::` followed by the same `;`-delimited connection + properties you validated with `cdatacli`. +- **`?` positional parameters** (`setString`/`setObject`); use `executeUpdate()` for + INSERT/UPDATE/DELETE. +- Under `ServiceLoader` no explicit driver registration is needed; if a non-standard + classloader misses it, register with `Class.forName("cdata.jdbc..Driver")`. + +### Connection string & auth (reuse discovery, keep secrets out of code) + +Build the URL from config, not hard-coded. For **OAuth** sources, point +`OAuthSettingsLocation` at the token file discovery already populated so the app refreshes +silently. Sources with **no embedded OAuth app** (e.g. Gmail) require the user's own +`OAuthClientId`/`OAuthClientSecret` even to refresh — read those from environment variables, +never commit them. + +--- + +## Step 5: End-to-End Example (Java) + +```java +import java.sql.*; +import java.util.*; + +public class Accounts { + private static final String URL = + "jdbc:salesforce:AuthScheme=OAuth;InitiateOAuth=GETANDREFRESH;UseSandbox=true;" + + "OAuthSettingsLocation=C:\\...\\OAuthSettings-sbx.txt"; + + static List list(String search, int limit) throws SQLException { + String sql = "SELECT Id, Name FROM Account" + + (search == null ? "" : " WHERE Name LIKE ?") + + " ORDER BY Name LIMIT " + limit; + try (Connection c = DriverManager.getConnection(URL); + PreparedStatement ps = c.prepareStatement(sql)) { + if (search != null) ps.setString(1, "%" + search + "%"); + try (ResultSet rs = ps.executeQuery()) { + List out = new ArrayList<>(); + while (rs.next()) out.add(rs.getString("Name")); + return out; + } + } + } + + static void create(String name, String industry) throws SQLException { + try (Connection c = DriverManager.getConnection(URL); + PreparedStatement ps = c.prepareStatement( + "INSERT INTO Account (Name, Industry) VALUES (?, ?)")) { + ps.setString(1, name); + ps.setString(2, industry); + ps.executeUpdate(); + } + } +} +``` + +--- + +## Troubleshooting + +| Error | Cause / fix | +|---|---| +| `No suitable driver found for jdbc::...` | The jar isn't on the classpath (or the URL prefix is wrong). Add `cdata.jdbc..jar` to the classpath / dependencies; confirm the URL starts with `jdbc::`. | +| `Could not find a valid license` | Jar not activated on this machine — activate via `cdatacli drivers activate` or the jar wizard (Step 2). The `.lic` must sit next to the jar or in `~/.CData/`. | +| `ClassNotFoundException: cdata.jdbc..Driver` | Only relevant if registering manually — the jar isn't on the classpath, or the class name is misspelled (`` is the cased driver name). | +| `'OAuthClientId' and 'OAuthClientSecret' are needed to initiate OAuth` | Source has no embedded OAuth app; add client id/secret (from env vars) to the JDBC URL, matching the app whose refresh token is cached. | diff --git a/skills/cdata-cli-odbc/SKILL.md b/skills/cdata-cli-odbc/SKILL.md new file mode 100644 index 0000000..dafdc6e --- /dev/null +++ b/skills/cdata-cli-odbc/SKILL.md @@ -0,0 +1,231 @@ +--- +name: cdata-cli-odbc +description: "Use when writing application code in Node.js / JavaScript / TypeScript — or any non-JVM language (Go, PHP, Ruby, Rust, C/C++) — that connects to a data source through a CData driver, after the connection and SQL have been validated with the cdata-cli discovery skill. Covers the CData ODBC Driver edition: confirming the system-installed driver, configuring a DSN or a DSN-less connection string, and connecting via the language's ODBC bindings (e.g. the Node `odbc` package). This is the cross-language build-phase companion to cdata-cli — the fallback for any environment that speaks ODBC." +license: MIT +metadata: + author: CData Software + version: "1.0" +--- + +# 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 + +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**). + +--- + +## 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: + +```powershell +# Windows +Get-OdbcDriver | Where-Object Name -like "*CData*" +# e.g. "CData ODBC Driver for Salesforce" (note the 32-bit / 64-bit rows) +``` + +```bash +# 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 \lib64\CData.ODBC..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 `cdatacli` connection doesn't mean this is activated.** `cdatacli` runs 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 + +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: + +```powershell +# 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 " ` + -DsnType User -Platform "64-bit" -SetPropertyValue @( + "AuthScheme=OAuth", + "InitiateOAuth=GETANDREFRESH", + "OAuthSettingsLocation=C:\...\CData\ Data Provider\OAuthSettings-xxx.txt" + ) +``` + +```ini +# Linux/macOS (unixODBC): register the driver in odbcinst.ini, then a DSN in odbc.ini +# ~/.odbc.ini +[MySource SBX] +Driver = CData ODBC Driver for +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: + +```text +Driver={CData ODBC Driver for };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`/`OAuthClientSecret` even 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. + +```javascript +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.query` for reads; the same for INSERT/UPDATE/DELETE. Use `odbc.connect(CS)` for a + single short-lived connection. + +### Other languages + +The pattern is identical everywhere ODBC is available: connect with `DSN=` (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): + +```javascript +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. | diff --git a/skills/cdata-cli-python/SKILL.md b/skills/cdata-cli-python/SKILL.md new file mode 100644 index 0000000..a4b8462 --- /dev/null +++ b/skills/cdata-cli-python/SKILL.md @@ -0,0 +1,257 @@ +--- +name: cdata-cli-python +description: "Use when writing Python application code that connects to a data source through a CData driver — after the connection and SQL have been validated with the cdata-cli discovery skill. Covers mapping the language to the CData Python Connector edition, downloading the connector wheel from CData's Python repository, activating its license, and connecting via DB-API 2.0 (cdata..connect). This is the Python build-phase companion to cdata-cli; invoke it whenever the target application language is Python (Salesforce, Snowflake, Jira, Gmail, etc.)." +license: MIT +metadata: + author: CData Software + version: "1.0" +--- + +# SKILL: CData CLI — Python 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 Python application code** using the **CData Python Connector**. + +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 the Python Connector. + +--- + +## Language / Driver Edition + +| Language | CData edition | Python import | +|---|---|---| +| **Python 3.x** | **CData Python Connector** | `import cdata.` | + +`` is the lowercased driver name — e.g. `cdata.salesforce`, `cdata.gmail`, +`cdata.snowflake`. The connector is a compiled DB-API 2.0 module (`.pyd`/`.so`) plus +SQLAlchemy dialects, shipped as a platform-specific wheel. + +--- + +## Prerequisite: Discovery Done First + +This skill assumes `cdata-cli` has already been used to: + +- confirm the source connects, and +- validate the exact tables/columns and the SQL the app will run. + +Reuse those results here. In particular, if discovery created a `cdatacli` connection that +performed an **OAuth** browser sign-in, note its `OAuthSettingsLocation` — the Python app +can point at the same cached token file and skip re-authenticating (see **Connect**). + +> The CLI's `drivers download` is JDBC-only and cannot fetch the Python wheel. The wheel +> comes from CData's Python repository over plain HTTP (below). + +--- + +## Step 1: Get the Connector Wheel + +CData Python Connectors are published at **`https://maven.cdata.com/python/`** as a +browseable index (plain HTTP — no API, no auth). + +1. Find the artifact: `cdata--connector` (e.g. `cdata-salesforce-connector`). +2. Open the newest version folder (e.g. `26.0.9655`). +3. Pick the wheel matching the target OS/arch: + +| Platform | Wheel suffix | +|---|---| +| Windows x64 | `...-win_amd64.whl` | +| Linux x64 | `...-linux_x86_64.whl` | +| macOS Apple Silicon | `...-macosx__arm64.whl` | + +The `cp310-abi3` tag means **Python 3.10+** with the stable ABI — one wheel covers all +Python ≥ 3.10. + +Download it (validate the file — the server occasionally drops a connection mid-transfer): + +```bash +# bash +curl -fSL --retry 3 --retry-all-errors -o cdata__connector--cp310-abi3-win_amd64.whl \ + "https://maven.cdata.com/p/python/cdata--connector//cdata__connector--cp310-abi3-win_amd64.whl" +``` + +```powershell +# PowerShell — curl.exe is more robust here than Invoke-WebRequest +curl.exe -fSL --retry 3 --retry-all-errors -o "cdata__connector--cp310-abi3-win_amd64.whl" ` + "https://maven.cdata.com/p/python/cdata--connector//cdata__connector--cp310-abi3-win_amd64.whl" +``` + +A `.whl` is a zip; if it won't open as an archive it truncated — re-download. + +**Already installed locally?** If the connector was installed via a CData product bundle +you can use that wheel instead of downloading — look under the extraction path: + +``` +\CData.Python.\win\Python\64\cdata__connector-...whl # Windows +/CData.Python./linux/cdata__connector-...tar.gz # Linux/macOS +``` + +--- + +## Step 2: Install the Connector + +Install the wheel into the interpreter the app will run: + +```bash +python -m pip install cdata__connector--cp310-abi3-win_amd64.whl +``` + +A virtualenv is **recommended** (keeps the install project-local and the license-tool path +predictable) but **not required** — a global install works the same: + +```bash +python -m venv .venv # optional; activate, then pip install into it +``` + +Verify the module imports: + +```bash +python -c "import cdata. as m; print('paramstyle:', m.paramstyle, '| apilevel:', m.apilevel)" +# expect: paramstyle: qmark | apilevel: 2.0 +``` + +--- + +## Step 3: Activate the License + +> **A working `cdatacli` connection doesn't mean this is activated.** `cdatacli` runs on the +> JDBC driver, so it only proves *JDBC* is licensed — the Python Connector is a separate license. + +The Python Connector is **licensed separately** from the JDBC/ADO.NET/ODBC editions, and +**per machine**. Without activation the first query raises *"Could not find a valid +license…"*. + +The activator ships inside the installed package, in the active interpreter's +`site-packages/cdata/installlic_/` (locate it with +`python -c "import cdata, os; print(os.path.dirname(cdata.__file__))"`): + +``` +installlic_/install-license.exe # Windows +installlic_/install-license.sh # macOS/Linux +``` + +It is **interactive** and must run **from its own folder** (it reads `prod.inf` from the +current directory). Because it prompts for input, **have the user run it in a normal +terminal — outside the AI coding session** (an AI session can't reliably answer the +prompts, and it hangs if run with no arguments). + +**Trial:** run it and answer the `Name:` and `Email:` prompts: + +```bash +cd folder> # path located above +.\install-license.exe # Windows; macOS/Linux: ./install-license.sh +# then answer the Name: and Email: prompts +``` + +**Purchased key (do this outside an AI session — the key is a secret):** + +```bash +cd folder> +.\install-license.exe YOUR-PRODUCT-KEY # Windows; macOS/Linux: ./install-license.sh YOUR-PRODUCT-KEY +``` + +Re-run the import/query check after activation to confirm it took. + +--- + +## Step 4: Connect (DB-API 2.0) + +```python +import cdata. as mod + +conn = mod.connect(CONNECTION_STRING) # a full CData connection string +cur = conn.cursor() +cur.execute("SELECT Id, Name FROM Account WHERE Name LIKE ?", ["%Acme%"]) +rows = cur.fetchall() +``` + +- **`paramstyle` is `qmark`** — use `?` placeholders and pass a list to `execute`. Always + parameterize user input; never string-format SQL. +- Call **`conn.commit()`** after INSERT/UPDATE/DELETE. + + +### Connection string & auth (reuse discovery, keep secrets out of code) + +Pass the same properties you validated with `cdatacli`. For **OAuth** sources, point +`OAuthSettingsLocation` at the token file the discovery connection already populated so the +app refreshes silently with no browser flow: + +```python +# Embedded-OAuth source (e.g. Salesforce) — cached token is sufficient: +cs = ("AuthScheme=OAuth;InitiateOAuth=GETANDREFRESH;UseSandbox=true;" + r"OAuthSettingsLocation=C:\...\CData\Salesforce Data Provider\OAuthSettings-xxx.txt") +``` +--- + +## Step 5: End-to-End Example + +Framework-agnostic (wrap in Flask/FastAPI/Tkinter/etc. as needed): + +```python +import cdata.salesforce as sf + +CS = ("AuthScheme=OAuth;InitiateOAuth=GETANDREFRESH;UseSandbox=true;" + r"OAuthSettingsLocation=C:\...\OAuthSettings-sbx.txt") + +def get_conn(): + return sf.connect(CS) + +def list_accounts(search=None, limit=50): + conn = get_conn() + try: + cur = conn.cursor() + sql = "SELECT Id, Name, Industry FROM Account" + params = [] + if search: + sql += " WHERE Name LIKE ?"; params.append(f"%{search}%") + sql += f" ORDER BY Name LIMIT {int(limit)}" + cur.execute(sql, params) + return cur.fetchall() + finally: + conn.close() + +def create_account(name, industry=None): + conn = get_conn() + try: + cur = conn.cursor() + cur.execute("INSERT INTO Account (Name, Industry) VALUES (?, ?)", [name, industry]) + conn.commit() + finally: + conn.close() + +if __name__ == "__main__": + for row in list_accounts(limit=5): + print(row) +``` + +--- + +## SQLAlchemy (optional) + +The wheel also installs SQLAlchemy dialects, so ORM/engine code works too: + +- `cdata.sqlalchemy_` — SQLAlchemy 1.4-style dialect +- `cdata.sqlalchemy2_` — SQLAlchemy 2.0-style dialect + +```python +from sqlalchemy import create_engine, text +# URL form: :///? +engine = create_engine("salesforce:///?AuthScheme=OAuth;InitiateOAuth=GETANDREFRESH;UseSandbox=true") +with engine.connect() as c: + print(c.execute(text("SELECT COUNT(*) FROM Account")).scalar()) +``` + +--- + +## Troubleshooting + +| Error | Cause / fix | +|---|---| +| `Could not find a valid license for using CData Python Driver for ` | Python Connector not activated on this machine — run `install-license` (Step 3). Separate from any JDBC/ADO.NET/ODBC license. | +| `'OAuthClientId' and 'OAuthClientSecret' are needed to initiate OAuth` | Source has no embedded OAuth app; supply client id/secret in the connection string (from env vars). Must match the app whose refresh token is cached. | +| `object does not support the context manager protocol` | Used `with mod.connect(...)`. Replace with explicit `try/finally`| +| `ModuleNotFoundError: No module named 'cdata.'` | Wheel not installed in the active interpreter — confirm the venv is active / the wheel installed, then re-check `import cdata.`. | +| `install-license.exe` hangs or errors on `prod.inf` | Run it **from its own folder** with a `Name`/`Email` (trial) or a product key argument; don't run it with no arguments. | diff --git a/skills/cdata-cli/SKILL.md b/skills/cdata-cli/SKILL.md index 5f8ebe6..07aed4c 100644 --- a/skills/cdata-cli/SKILL.md +++ b/skills/cdata-cli/SKILL.md @@ -328,14 +328,23 @@ cdatacli query sql --connection --sql "EXEC ProcedureName Param1='value1' ### Step 8: Generate Application Code -If the user's goal includes generating application code, use the validated SQL, driver path (from `drivers list`), and connection string to generate standalone code. - -#### Driver Locations - -**JDBC (Java):** -- Windows: `C:\Program Files\CData\CData JDBC Driver for \lib\cdata.jdbc..jar` -- macOS: `/Applications/CData/CData JDBC Driver for /lib/cdata.jdbc..jar` -- CLI-bundled: `./` or `./lib/cdata.jdbc..jar` next to the CLI executable -- Driver class: `cdata.jdbc..Driver` -- JDBC URL: `jdbc::` -- License file: same directory, `cdata.jdbc..lic` +Steps 1–7 (connect, discover schema, validate SQL) are language-agnostic and are this +skill's job. **Once the goal shifts from exploring data to writing application code, hand +off to the specialized building skill for the target driver technology.** Those skills +specify the non-obvious, per-technology specifics this discovery skill deliberately leaves +out: where to obtain the driver (NuGet, CData's Python repo, system install), how to +license it, the connection idiom, and language-specific gotchas. Carry the validated SQL +and (non-secret) connection details from Steps 1–7 into whichever skill you invoke. + +**Pick the building skill by the app's language / driver technology:** + +| Building in… | Invoke skill | Driver technology | +|---|---|---| +| **Python** | `cdata-cli-python` | CData Python Connector | +| **C# / .NET** (also VB.NET, F#) | `cdata-cli-adonet` | ADO.NET Data Provider | +| **Node.js / JavaScript / TypeScript**, or **any non-JVM language** (Go, PHP, Ruby, Rust, C/C++) | `cdata-cli-odbc` | ODBC Driver | +| **Java / JVM** (also Kotlin, Scala) | `cdata-cli-java` | JDBC Driver | + +Invoke the matching building skill **before** writing code. Default to the +language's native technology and treat `cdata-cli-odbc` +as the cross-language fallback. \ No newline at end of file