Skip to content

C:\> Documentation

03

Connecting your database

Contents

Codepanion runs read-only queries against your production database so the agent can look up records, check statuses, and investigate data issues. We support SQL Server and PostgreSQL.

Good to know

Every query the agent sends is a SELECT compiled by Codepanion, and it runs inside a transaction that is always rolled back, never committed. On PostgreSQL the session is also read-only (default_transaction_read_only=on). On SQL Server, Codepanion sets ApplicationIntent=ReadOnly, but that only routes the connection to a readable secondary where one exists; it does not stop a login with write permission from writing. The one limit your database enforces itself is the permissions of the user you connect as, so give Codepanion a dedicated user that can only read.

Requirements

A direct connection is dialled from Codepanion's cloud, so the connection string has to meet these rules. Test Connection names the rule when a setting in the string breaks one.

  • SQL authentication. A user name and password in the string. Integrated Security and the Microsoft Entra authentication modes are refused. An Azure SQL server set to Microsoft Entra authentication only cannot be connected directly.
  • A public TCP host. A host name or address that resolves to a public address. Private and loopback addresses, named pipes and PostgreSQL socket paths are refused.
  • Inbound access. Your firewall has to accept connections from Azure on the database port. Codepanion does not connect from a fixed IP address you can add to an allow-list.
  • No read-only settings of your own. Leave out SQL Server's ApplicationIntent and PostgreSQL's Options. Codepanion sets both itself and refuses a string that already carries them.

If any of these rules out your database, run the on-prem connector instead. It runs inside your network and dials the database from there, so nothing needs to be opened to inbound traffic and no connection string is sent to Codepanion.

SQL Server

Give us a standard connection string with SQL authentication. If you run an availability group or Azure SQL read scale-out, the ApplicationIntent=ReadOnly Codepanion adds sends the agent's queries to a readable secondary; otherwise they reach the server you name.

SQL Server connection string
Server=your-server.database.windows.net;Database=yourdb;User Id=codepanion_reader;Password=...;

Create a dedicated user with only SELECT on the tables the agent needs. On SQL Server this is required rather than recommended, since ApplicationIntent does not make a connection read-only. On Azure SQL Database, or any contained database, the user carries its own password:

sql · azure sql database
CREATE USER codepanion_reader WITH PASSWORD = '...';
GRANT SELECT ON SCHEMA::dbo TO codepanion_reader;

On SQL Server installed on your own machines or VMs, where databases are not contained, the password belongs to a server login and the database user maps to it:

sql · sql server
CREATE LOGIN codepanion_reader WITH PASSWORD = '...';
USE yourdb;
CREATE USER codepanion_reader FOR LOGIN codepanion_reader;
GRANT SELECT ON SCHEMA::dbo TO codepanion_reader;

PostgreSQL

Same idea. Give us a standard connection string; we set default_transaction_read_only=on at the session level. A read replica is ideal but not mandatory.

PostgreSQL connection string
Host=your-server.example.com;Database=yourdb;Username=codepanion_reader;Password=...;

Create a dedicated role with only SELECT on the schemas the agent should see:

sql
CREATE ROLE codepanion_reader WITH LOGIN PASSWORD '...';
GRANT USAGE ON SCHEMA public TO codepanion_reader;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO codepanion_reader;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
  GRANT SELECT ON TABLES TO codepanion_reader;

Where to configure

Open Settings → Database Connections in the Codepanion app and paste your connection string. It's encrypted at rest in Azure Key Vault and only accessed by the agent at query time; Test Connection proves it works before you save.

One database connection per environment
Settings → Database Connections. Every connection is filed under an environment label and shows its provider and the last time it tested green.
One database connection per environment
Screenshot of One database connection per environment. Settings → Database Connections. Every connection is filed under an environment label and shows its provider and the last time it tested green.
Settings → Database Connections. Every connection is filed under an environment label and shows its provider and the last time it tested green.

Each connection is filed under an environment label of lowercase letters, digits and hyphens. If you only have one database, leave it on default and forget it exists; if you investigate across QA, staging and production, add one connection per label and the agent picks the right one per investigation. Providers can differ between them: SQL Server for one environment, PostgreSQL for another.

Kind is Static for the usual case: one fixed connection string. Choose Catalog instead when you're multi-tenant yourself and the real connection string has to be looked up per tenant from your own registry database; see catalog databases.

How queries are built

The agent never writes SQL. It emits a structured query: a table, the columns it wants, filters, ordering, an optional grouping with aggregates (including per-day, per-week and per-month buckets on a date column), an optional join, and a row limit. Codepanion checks every table and column name against the schema it discovered from your database, refuses anything that isn't there, and compiles the rest into parameterised SQL.

what the agent asked for
{
  "table": "payment_attempts",
  "columns": ["id", "status", "error_code", "region", "created_at"],
  "where": [
    { "column": "status", "op": "=", "value": "failed" },
    { "column": "region", "op": "=", "value": "eu-west-1", "logic": "and" }
  ],
  "limit": 50
}

becomes, on a SQL Server connection:

what your database ran
SELECT TOP 50 [payment_attempts].[id], [payment_attempts].[status],
  [payment_attempts].[error_code], [payment_attempts].[region],
  [payment_attempts].[created_at]
FROM [dbo].[payment_attempts]
WHERE [payment_attempts].[status] = @p0 AND [payment_attempts].[region] = @p1

Codepanion reads every schema the login can see, so the grants you give it decide what the agent can query. Tables in dbo (SQL Server) or public (PostgreSQL) keep their plain name; a table in any other schema is named schema.table, which is how the agent sees it listed. Every identifier is quoted (brackets on SQL Server, double quotes on PostgreSQL), so names with spaces or punctuation such as Order Details work as they are. The table is also qualified with the schema that discovery found it in, so a login whose default schema differs cannot steer the query at a different object. The statement is sent as one line; it is wrapped here for reading.

Values travel as parameters, never spliced into the statement, so no string the model produced is ever executed as SQL. The shape of the query is fixed too: SELECT only, capped at 1000 rows and at a total result size, run under a short command timeout inside a transaction that is always rolled back. There is no free-text SQL path for the agent to reach for, so there is nothing for injected text to turn into a statement of its own.

Seeing what ran

Nothing about the agent's access to your database is hidden. Every query appears in the investigation as a tool step, summarised in a line or two and opening onto the whole exchange: the environment it targeted, the structured query it sent, the SQL that was generated from it, the row count, and the rows that came back. An opened step is pictured under reading tool steps.

Computing without handing over the rows

Some questions need arithmetic over more rows than anyone wants in a conversation. The agent can send a result set straight to a sandbox in your own browser instead of into its own context, compute over it there, and keep only the answer. The model gets a description of the data rather than the data: a row count, the column names, and up to five sample rows. The full result set is never persisted into the investigation. How it works, and its limits, are in computing over results.

Query plans for slow lookups

When the agent runs a query it can also capture the database's estimated execution plan (SHOWPLAN_XML on SQL Server, EXPLAIN without ANALYZE on PostgreSQL), which is handy when an investigation is about a slow query. Estimated rather than actual on purpose: the statement is compiled but never executed, so capturing a plan cannot itself touch your data. Plan capture is best-effort and never blocks the result: queries run under a short timeout with a single polite retry, so the agent always gets its rows even if the plan can't be fetched.

Need help getting set up?

We do the first setup on a call with you: the CI step, the connection string, and a first investigation against your real code.

Or email us at hello@codepanion.app