diff --git a/docs-site/content/docs/cli-reference/ktx-connection.mdx b/docs-site/content/docs/cli-reference/ktx-connection.mdx index 0cec3eae..68b7f496 100644 --- a/docs-site/content/docs/cli-reference/ktx-connection.mdx +++ b/docs-site/content/docs/cli-reference/ktx-connection.mdx @@ -63,8 +63,7 @@ agents. "connections": [ { "id": "my-warehouse", - "driver": "postgres", - "readonly": false + "driver": "postgres" } ] } diff --git a/docs-site/content/docs/integrations/context-sources.mdx b/docs-site/content/docs/integrations/context-sources.mdx index 5b85bff2..be72c941 100644 --- a/docs-site/content/docs/integrations/context-sources.mdx +++ b/docs-site/content/docs/integrations/context-sources.mdx @@ -23,7 +23,6 @@ Agents should configure and ingest context sources in this order: | Field | Required | Description | |-------|----------|-------------| | `driver` | Yes | Source adapter: `dbt`, `metricflow`, `lookml`, `metabase`, `looker`, or `notion` | -| `readonly` | Strongly recommended | Marks the source as read-only for KTX | | `source_dir` | For local file sources | Absolute or project-relative source directory | | `repo_url` | For Git-hosted sources | Git repository URL | | `branch` | No | Git branch to read | @@ -49,7 +48,6 @@ connections: my-dbt: driver: dbt source_dir: /path/to/dbt/project - readonly: true ``` For a Git-hosted project: @@ -62,7 +60,6 @@ connections: branch: main path: analytics/dbt # For monorepos auth_token_ref: env:GITHUB_TOKEN - readonly: true ``` ### Authentication @@ -110,7 +107,6 @@ connections: branch: main path: dbt_metrics # Subdirectory for monorepos auth_token_ref: env:GITHUB_TOKEN - readonly: true ``` For a local path: @@ -157,7 +153,6 @@ connections: branch: main path: analytics # Subdirectory for monorepos auth_token_ref: env:GITHUB_TOKEN - readonly: true ``` For a local path: @@ -219,7 +214,6 @@ connections: syncEnabled: "3": true syncMode: ONLY # Only ingest mapped databases - readonly: true ``` ### Authentication @@ -276,7 +270,6 @@ connections: mappings: connectionMappings: postgres_connection: postgres-main # Looker conn → KTX conn - readonly: true ``` ### Authentication @@ -329,7 +322,6 @@ connections: crawl_mode: selected_roots root_page_ids: - "abc123def456..." - readonly: true ``` For crawling all accessible pages: @@ -340,7 +332,6 @@ connections: driver: notion auth_token_ref: env:NOTION_TOKEN crawl_mode: all_accessible - readonly: true ``` ### Authentication diff --git a/docs-site/content/docs/integrations/primary-sources.mdx b/docs-site/content/docs/integrations/primary-sources.mdx index 94dc4e44..653c4e38 100644 --- a/docs-site/content/docs/integrations/primary-sources.mdx +++ b/docs-site/content/docs/integrations/primary-sources.mdx @@ -21,7 +21,6 @@ Agents should prefer environment or file references over literal secrets. | `url` | One of the connection methods | URL-style connectors | Database URL, `env:NAME`, or `file:/path/to/secret` | | `host`, `port`, `database`, `username`, `password` | One of the connection methods | PostgreSQL, MySQL, ClickHouse, SQL Server | Field-by-field connection values | | `schema` or `schemas` | No | schema-aware warehouses | Single schema or list of schemas to scan | -| `readonly` | Strongly recommended | all primary sources | Marks the connection as read-only in KTX config | | `historicSql` | No | supported warehouses | Enables query-history ingestion when the warehouse supports it | | `path` | Yes for path-style SQLite | SQLite | Local SQLite database path or `env:NAME` reference | @@ -37,7 +36,6 @@ connections: driver: postgres url: postgresql://user:password@host:5432/database schema: public - readonly: true ``` Or with individual fields: @@ -55,7 +53,6 @@ connections: - public - analytics ssl: true - readonly: true ``` ### Authentication @@ -123,7 +120,6 @@ connections: username: KTX_SERVICE password: env:SNOWFLAKE_PASSWORD role: ANALYST - readonly: true ``` For multiple schemas: @@ -196,7 +192,6 @@ connections: credentials_json: file:~/.config/gcloud/bq-service-account.json dataset_id: analytics location: US - readonly: true ``` For multiple datasets: @@ -269,7 +264,6 @@ connections: my-clickhouse: driver: clickhouse url: http://localhost:8123/analytics - readonly: true ``` Or with individual fields: @@ -284,7 +278,6 @@ connections: username: default password: env:CH_PASSWORD ssl: false - readonly: true ``` ### Authentication @@ -328,7 +321,6 @@ connections: my-mysql: driver: mysql url: mysql://user:password@host:3306/database - readonly: true ``` Or with individual fields: @@ -343,7 +335,6 @@ connections: username: ktx_reader password: env:MYSQL_PASSWORD ssl: true - readonly: true ``` ### Authentication @@ -387,7 +378,6 @@ connections: my-sqlserver: driver: sqlserver url: mssql://user:password@host:1433/database?trustServerCertificate=true - readonly: true ``` Or with individual fields: @@ -403,7 +393,6 @@ connections: password: env:MSSQL_PASSWORD schema: dbo trustServerCertificate: true - readonly: true ``` For multiple schemas: @@ -455,7 +444,6 @@ connections: my-sqlite: driver: sqlite path: ./data/warehouse.sqlite - readonly: true ``` Path supports multiple formats: diff --git a/examples/local-warehouse/ktx.yaml b/examples/local-warehouse/ktx.yaml index 00ccffbd..7e814188 100644 --- a/examples/local-warehouse/ktx.yaml +++ b/examples/local-warehouse/ktx.yaml @@ -2,7 +2,6 @@ project: local-warehouse connections: warehouse: driver: postgres - readonly: true storage: state: sqlite search: sqlite-fts5 diff --git a/examples/orbit-relationship-verification/ktx.yaml b/examples/orbit-relationship-verification/ktx.yaml index 5f826daf..082e0835 100644 --- a/examples/orbit-relationship-verification/ktx.yaml +++ b/examples/orbit-relationship-verification/ktx.yaml @@ -3,7 +3,6 @@ connections: orbit: driver: sqlite path: ../../packages/context/test/fixtures/relationship-benchmarks/orbit_style_product_no_declared_constraints/data.sqlite - readonly: true storage: state: sqlite search: sqlite-fts5 diff --git a/packages/cli/src/connection.test.ts b/packages/cli/src/connection.test.ts index 57ed8742..6eb3a08c 100644 --- a/packages/cli/src/connection.test.ts +++ b/packages/cli/src/connection.test.ts @@ -94,7 +94,7 @@ describe('runKtxConnection', () => { const projectDir = join(tempDir, 'project'); await initKtxProject({ projectDir, projectName: 'warehouse' }); await writeConnections(projectDir, { - warehouse: { driver: 'postgres', url: 'env:DATABASE_URL', readonly: true }, + warehouse: { driver: 'postgres', url: 'env:DATABASE_URL' }, docs: { driver: 'notion', auth_token_ref: 'env:NOTION_TOKEN', crawl_mode: 'all_accessible' }, }); const io = makeIo(); @@ -123,7 +123,7 @@ describe('runKtxConnection', () => { const projectDir = join(tempDir, 'project'); await initKtxProject({ projectDir, projectName: 'warehouse' }); await writeConnections(projectDir, { - warehouse: { driver: 'sqlite', readonly: true }, + warehouse: { driver: 'sqlite' }, }); const { connector, introspect, cleanup } = nativeConnector('sqlite', ['customers', 'orders']); const createScanConnector = vi.fn(async () => connector); @@ -202,7 +202,7 @@ describe('runKtxConnection', () => { const projectDir = join(tempDir, 'project'); await initKtxProject({ projectDir, projectName: 'warehouse' }); await writeConnections(projectDir, { - warehouse: { driver: 'sqlite', readonly: true }, + warehouse: { driver: 'sqlite' }, }); const cleanup = vi.fn(async () => undefined); const connector: KtxScanConnector = { diff --git a/packages/cli/src/demo-assets.ts b/packages/cli/src/demo-assets.ts index 1e972ef7..aae9f1a2 100644 --- a/packages/cli/src/demo-assets.ts +++ b/packages/cli/src/demo-assets.ts @@ -57,7 +57,6 @@ function demoConfig(databasePath: string): string { ` ${DEMO_CONNECTION_ID}:`, ' driver: sqlite', ` path: ${JSON.stringify(databasePath)}`, - ' readonly: true', 'storage:', ' state: sqlite', ' search: sqlite-fts5', diff --git a/packages/cli/src/doctor.test.ts b/packages/cli/src/doctor.test.ts index d0ebdb95..cd96e6d2 100644 --- a/packages/cli/src/doctor.test.ts +++ b/packages/cli/src/doctor.test.ts @@ -275,7 +275,6 @@ describe('runKtxDoctor', () => { ' warehouse:', ' driver: postgres', ' url: env:WAREHOUSE_DATABASE_URL', - ' readonly: true', ' historicSql:', ' enabled: true', ' dialect: postgres', diff --git a/packages/cli/src/historic-sql-doctor.test.ts b/packages/cli/src/historic-sql-doctor.test.ts index b6f5f0fa..f3bc347e 100644 --- a/packages/cli/src/historic-sql-doctor.test.ts +++ b/packages/cli/src/historic-sql-doctor.test.ts @@ -25,7 +25,7 @@ describe('runPostgresHistoricSqlDoctorChecks', () => { it('passes when no Postgres historic-SQL connections are enabled', async () => { const checks = await runPostgresHistoricSqlDoctorChecks( projectWithConnections({ - warehouse: { driver: 'sqlite', path: './warehouse.db', readonly: true }, + warehouse: { driver: 'sqlite', path: './warehouse.db' }, }), { postgresHistoricSqlProbe: vi.fn(), @@ -53,7 +53,6 @@ describe('runPostgresHistoricSqlDoctorChecks', () => { warehouse: { driver: 'postgres', url: 'env:WAREHOUSE_DATABASE_URL', - readonly: true, historicSql: { enabled: true, dialect: 'postgres' }, }, }), @@ -66,7 +65,6 @@ describe('runPostgresHistoricSqlDoctorChecks', () => { connection: { driver: 'postgres', url: 'env:WAREHOUSE_DATABASE_URL', - readonly: true, historicSql: { enabled: true, dialect: 'postgres' }, }, env: process.env, @@ -87,7 +85,6 @@ describe('runPostgresHistoricSqlDoctorChecks', () => { warehouse: { driver: 'postgres', url: 'env:WAREHOUSE_DATABASE_URL', - readonly: true, historicSql: { enabled: true, dialect: 'postgres' }, }, }), @@ -119,7 +116,6 @@ describe('runPostgresHistoricSqlDoctorChecks', () => { warehouse: { driver: 'postgres', url: 'env:WAREHOUSE_DATABASE_URL', - readonly: true, historicSql: { enabled: true, dialect: 'postgres' }, }, }), @@ -154,7 +150,6 @@ describe('runPostgresHistoricSqlDoctorChecks', () => { warehouse: { driver: 'mysql', url: 'env:WAREHOUSE_DATABASE_URL', - readonly: true, historicSql: { enabled: true, dialect: 'postgres' }, }, }), @@ -180,7 +175,6 @@ describe('runPostgresHistoricSqlDoctorChecks', () => { warehouse: { driver: 'postgres', url: 'env:WAREHOUSE_DATABASE_URL', - readonly: true, historicSql: { enabled: true, dialect: 'postgres' }, }, }), diff --git a/packages/cli/src/historic-sql-doctor.ts b/packages/cli/src/historic-sql-doctor.ts index c83e7fb4..bb9a681c 100644 --- a/packages/cli/src/historic-sql-doctor.ts +++ b/packages/cli/src/historic-sql-doctor.ts @@ -86,8 +86,9 @@ async function defaultPostgresHistoricSqlProbe( const [{ PostgresPgssReader }, { KtxPostgresHistoricSqlQueryClient, isKtxPostgresConnectionConfig }] = await Promise.all([import('@ktx/context/ingest'), import('@ktx/connector-postgres')]); + const inputDriver = input.connection.driver ?? 'unknown'; if (!isKtxPostgresConnectionConfig(input.connection)) { - throw new Error(`Native PostgreSQL connector cannot run driver "${input.connection.driver ?? 'unknown'}"`); + throw new Error(`Native PostgreSQL connector cannot run driver "${inputDriver}"`); } const client = new KtxPostgresHistoricSqlQueryClient({ diff --git a/packages/cli/src/local-adapters.test.ts b/packages/cli/src/local-adapters.test.ts index 517c0588..b7491920 100644 --- a/packages/cli/src/local-adapters.test.ts +++ b/packages/cli/src/local-adapters.test.ts @@ -45,7 +45,6 @@ describe('CLI local ingest adapters', () => { ' warehouse:', ' driver: postgres', ' url: env:WAREHOUSE_DATABASE_URL', - ' readonly: true', ' historicSql:', ' enabled: true', ' dialect: postgres', @@ -76,7 +75,6 @@ describe('CLI local ingest adapters', () => { 'connections:', ' bq:', ' driver: bigquery', - ' readonly: true', ' dataset_id: analytics', ' location: us', ' credentials_json: \'{"project_id":"demo-project"}\'', @@ -110,7 +108,6 @@ describe('CLI local ingest adapters', () => { 'connections:', ' sf:', ' driver: snowflake', - ' readonly: true', ' account: acct', ' warehouse: wh', ' database: ANALYTICS', diff --git a/packages/cli/src/local-adapters.ts b/packages/cli/src/local-adapters.ts index 9a6915c2..010a7188 100644 --- a/packages/cli/src/local-adapters.ts +++ b/packages/cli/src/local-adapters.ts @@ -190,10 +190,9 @@ function enabledHistoricSqlDialect(connection: unknown): 'postgres' | 'bigquery' function createEphemeralPostgresHistoricSqlClient(project: KtxLocalProject, connectionId: string) { const connection = project.config.connections[connectionId] as KtxPostgresConnectionConfig | undefined; + const inputDriver = connection?.driver ?? 'unknown'; if (!isKtxPostgresConnectionConfig(connection)) { - throw new Error( - `Historic SQL local ingest requires a Postgres connection, got ${String(connection?.driver ?? 'unknown')}`, - ); + throw new Error(`Historic SQL local ingest requires a Postgres connection, got ${String(inputDriver)}`); } return { async executeQuery(sql: string, params?: unknown[]) { @@ -212,10 +211,9 @@ function createEphemeralPostgresHistoricSqlClient(project: KtxLocalProject, conn function createEphemeralBigQueryHistoricSqlClient(project: KtxLocalProject, connectionId: string) { const connection = project.config.connections[connectionId] as KtxBigQueryConnectionConfig | undefined; + const inputDriver = connection?.driver ?? 'unknown'; if (!isKtxBigQueryConnectionConfig(connection)) { - throw new Error( - `Historic SQL local ingest requires a BigQuery connection, got ${String(connection?.driver ?? 'unknown')}`, - ); + throw new Error(`Historic SQL local ingest requires a BigQuery connection, got ${String(inputDriver)}`); } return { async executeQuery(query: string) { @@ -243,10 +241,9 @@ async function createEphemeralSnowflakeHistoricSqlClient( connectorModule: SnowflakeConnectorModule, ) { const connection = project.config.connections[connectionId]; + const inputDriver = connection?.driver ?? 'unknown'; if (!connectorModule.isKtxSnowflakeConnectionConfig(connection)) { - throw new Error( - `Historic SQL local ingest requires a Snowflake connection, got ${String(connection?.driver ?? 'unknown')}`, - ); + throw new Error(`Historic SQL local ingest requires a Snowflake connection, got ${String(inputDriver)}`); } return { async executeQuery(query: string) { @@ -308,10 +305,9 @@ function historicSqlOptionsForLocalRun(project: KtxLocalProject, options: KtxCli } if (dialect === 'bigquery') { + const inputDriver = connection?.driver ?? 'unknown'; if (!isKtxBigQueryConnectionConfig(connection)) { - throw new Error( - `Historic SQL local ingest requires a BigQuery connection, got ${String(connection?.driver ?? 'unknown')}`, - ); + throw new Error(`Historic SQL local ingest requires a BigQuery connection, got ${String(inputDriver)}`); } return { ...base, diff --git a/packages/cli/src/local-scan-connectors.test.ts b/packages/cli/src/local-scan-connectors.test.ts index 087e978d..e8a5c1e9 100644 --- a/packages/cli/src/local-scan-connectors.test.ts +++ b/packages/cli/src/local-scan-connectors.test.ts @@ -49,7 +49,6 @@ describe('createKtxCliScanConnector', () => { ' warehouse:', ' driver: sqlite', ' path: warehouse.db', - ' readonly: true', '', ].join('\n'), 'utf-8', @@ -72,7 +71,6 @@ describe('createKtxCliScanConnector', () => { ' warehouse:', ' driver: bigquery', ' dataset_id: analytics', - ' readonly: true', ' max_bytes_billed: "987654321"', '', ].join('\n'), @@ -123,7 +121,6 @@ describe('createKtxCliScanConnector', () => { ' warehouse:', ' type: postgres', ' url: postgresql://example/db', - ' readonly: true', '', ].join('\n'), 'utf-8', diff --git a/packages/cli/src/scan.test.ts b/packages/cli/src/scan.test.ts index 28c60ea0..c4cbaf70 100644 --- a/packages/cli/src/scan.test.ts +++ b/packages/cli/src/scan.test.ts @@ -861,7 +861,6 @@ describe('runKtxScan', () => { ' warehouse:', ' driver: mysql', ' url: env:MYSQL_URL', - ' readonly: true', '', ].join('\n'), 'utf-8', @@ -910,7 +909,6 @@ describe('runKtxScan', () => { ' warehouse:', ' driver: sqlite', ' path: warehouse.db', - ' readonly: true', '', ].join('\n'), 'utf-8', @@ -968,7 +966,6 @@ describe('runKtxScan', () => { ' database: analytics', ' username: reader', ' password: env:POSTGRES_PASSWORD', - ' readonly: true', '', ].join('\n'), 'utf-8', @@ -1035,7 +1032,6 @@ describe('runKtxScan', () => { ' database: analytics', ' username: reader', ' password: env:CLICKHOUSE_PASSWORD', - ' readonly: true', '', ].join('\n'), 'utf-8', @@ -1087,7 +1083,6 @@ describe('runKtxScan', () => { ' database: analytics', ' username: reader', ' schema: dbo', - ' readonly: true', '', ].join('\n'), 'utf-8', @@ -1153,7 +1148,6 @@ describe('runKtxScan', () => { ' dataset_id: analytics', ' credentials_json: env:BIGQUERY_CREDENTIALS_JSON', ' location: US', - ' readonly: true', '', ].join('\n'), 'utf-8', @@ -1222,7 +1216,6 @@ describe('runKtxScan', () => { ' database: ANALYTICS', ' schema_name: PUBLIC', ' username: reader', - ' readonly: true', '', ].join('\n'), 'utf-8', diff --git a/packages/cli/src/setup-databases.test.ts b/packages/cli/src/setup-databases.test.ts index 95d1e3fb..d010a908 100644 --- a/packages/cli/src/setup-databases.test.ts +++ b/packages/cli/src/setup-databases.test.ts @@ -218,7 +218,6 @@ describe('setup databases step', () => { ' warehouse:', ' driver: postgres', ' url: env:DATABASE_URL', - ' readonly: true', '', ].join('\n'), 'utf-8', @@ -281,7 +280,6 @@ describe('setup databases step', () => { expect(config.connections['postgres-warehouse']).toEqual({ driver: 'postgres', url: 'env:DATABASE_URL', - readonly: true, }); }); @@ -542,7 +540,6 @@ describe('setup databases step', () => { ' warehouse:', ' driver: postgres', ' url: env:DATABASE_URL', - ' readonly: true', 'setup:', ' database_connection_ids:', ' - warehouse', @@ -583,7 +580,6 @@ describe('setup databases step', () => { ' warehouse:', ' driver: postgres', ' url: env:DATABASE_URL', - ' readonly: true', 'setup:', ' database_connection_ids:', ' - warehouse', @@ -698,7 +694,6 @@ describe('setup databases step', () => { ' warehouse:', ' driver: postgres', ' url: env:DATABASE_URL', - ' readonly: true', 'setup:', ' database_connection_ids:', ' - warehouse', @@ -843,7 +838,6 @@ describe('setup databases step', () => { port: 5432, database: 'analytics', username: 'readonly', - readonly: true, }); expect(connection.password).toMatch(/^file:/); const secretPath = join(tempDir, '.ktx/secrets/postgres-warehouse-password'); @@ -998,7 +992,6 @@ describe('setup databases step', () => { expect(config.connections['postgres-warehouse']).toMatchObject({ driver: 'postgres', url: 'env:DATABASE_URL', - readonly: true, }); }); @@ -1115,7 +1108,6 @@ describe('setup databases step', () => { driver: 'postgres', url: 'env:DATABASE_URL', schemas: ['public'], - readonly: true, }); expect(config.setup).toEqual({ database_connection_ids: ['warehouse'], @@ -1153,7 +1145,6 @@ describe('setup databases step', () => { expect(config.connections.warehouse).toEqual({ driver: 'sqlite', path: './warehouse.sqlite', - readonly: true, }); expect(config.setup).toEqual({ database_connection_ids: ['warehouse'], @@ -1170,7 +1161,6 @@ describe('setup databases step', () => { ' warehouse:', ' driver: postgres', ' url: env:DATABASE_URL', - ' readonly: true', ' analytics:', ' driver: snowflake', ' authMethod: password', @@ -1180,7 +1170,6 @@ describe('setup databases step', () => { ' schema_name: PUBLIC', ' username: reader', ' password: env:SNOWFLAKE_PASSWORD', - ' readonly: true', '', ].join('\n'), 'utf-8', @@ -1443,7 +1432,6 @@ describe('setup databases step', () => { ' driver: bigquery', ' dataset_id: analytics', ' credentials_json: env:BIGQUERY_CREDENTIALS_JSON', - ' readonly: true', '', ].join('\n'), 'utf-8', @@ -1492,7 +1480,6 @@ describe('setup databases step', () => { ' warehouse:', ' driver: postgres', ' url: env:DATABASE_URL', - ' readonly: true', '', ].join('\n'), 'utf-8', diff --git a/packages/cli/src/setup-databases.ts b/packages/cli/src/setup-databases.ts index 85be2620..5b5b5f8a 100644 --- a/packages/cli/src/setup-databases.ts +++ b/packages/cli/src/setup-databases.ts @@ -593,7 +593,6 @@ async function buildFieldsConnectionConfig(input: { username, ...(passwordRef ? { password: passwordRef } : {}), ...(input.args.databaseSchemas.length > 0 ? { schemas: input.args.databaseSchemas } : {}), - readonly: true, }; } @@ -615,7 +614,6 @@ async function buildPastedUrlConnectionConfig(input: { driver: input.driver, url, ...(input.args.databaseSchemas.length > 0 ? { schemas: input.args.databaseSchemas } : {}), - readonly: true, }; } @@ -629,7 +627,6 @@ async function buildPastedUrlConnectionConfig(input: { driver: input.driver, url: ref, ...(input.args.databaseSchemas.length > 0 ? { schemas: input.args.databaseSchemas } : {}), - readonly: true, }; } @@ -637,7 +634,6 @@ async function buildPastedUrlConnectionConfig(input: { driver: input.driver, url, ...(input.args.databaseSchemas.length > 0 ? { schemas: input.args.databaseSchemas } : {}), - readonly: true, }; } @@ -661,14 +657,12 @@ async function buildUrlConnectionConfig(input: { driver: input.driver, url: ref, ...(input.args.databaseSchemas.length > 0 ? { schemas: input.args.databaseSchemas } : {}), - readonly: true, }; } return { driver: input.driver, url, ...(input.args.databaseSchemas.length > 0 ? { schemas: input.args.databaseSchemas } : {}), - readonly: true, }; } @@ -706,7 +700,7 @@ async function buildConnectionConfig(input: { 'SQLite database file\nEnter a relative or absolute path, for example ./warehouse.sqlite.', )); if (path === undefined) return 'back'; - return path ? { driver: 'sqlite', path, readonly: true } : null; + return path ? { driver: 'sqlite', path } : null; } if (driver === 'postgres' || driver === 'mysql' || driver === 'clickhouse' || driver === 'sqlserver') { return await buildUrlConnectionConfig({ driver, connectionId: input.connectionId, args, prompts }); @@ -728,7 +722,6 @@ async function buildConnectionConfig(input: { dataset_id: datasetId, credentials_json: normalizeFileReference(credentialsPath), ...(location ? { location } : {}), - readonly: true, }; } if (driver === 'snowflake') { @@ -767,7 +760,6 @@ async function buildConnectionConfig(input: { username, password: passwordRef, ...(role ? { role } : {}), - readonly: true, }; } throw new Error(`Unsupported database driver: ${driver}`); diff --git a/packages/cli/src/setup-sources.test.ts b/packages/cli/src/setup-sources.test.ts index c0fb0227..7fe61f76 100644 --- a/packages/cli/src/setup-sources.test.ts +++ b/packages/cli/src/setup-sources.test.ts @@ -98,7 +98,7 @@ describe('setup sources step', () => { ...config, connections: { ...config.connections, - warehouse: { driver: 'postgres', url: 'env:DATABASE_URL', readonly: true }, + warehouse: { driver: 'postgres', url: 'env:DATABASE_URL' }, }, setup: { ...config.setup, @@ -455,7 +455,6 @@ describe('setup sources step', () => { driver: 'snowflake', account: 'acme', database: 'analytics', - readonly: true, }); const cases: Array<{ diff --git a/packages/cli/src/setup.test.ts b/packages/cli/src/setup.test.ts index dd134fce..246484d3 100644 --- a/packages/cli/src/setup.test.ts +++ b/packages/cli/src/setup.test.ts @@ -170,7 +170,6 @@ describe('setup status', () => { ' warehouse:', ' driver: postgres', ' url: env:DATABASE_URL', - ' readonly: true', '', ].join('\n'), 'utf-8', @@ -192,7 +191,6 @@ describe('setup status', () => { ' warehouse:', ' driver: postgres', ' url: env:DATABASE_URL', - ' readonly: true', '', ].join('\n'), 'utf-8', @@ -1373,7 +1371,6 @@ describe('setup status', () => { ' warehouse:', ' driver: postgres', ' url: env:DEMO_DATABASE_URL', - ' readonly: true', '', ].join('\n'), 'utf-8', diff --git a/packages/cli/src/sl.test.ts b/packages/cli/src/sl.test.ts index ff4132b4..14f18337 100644 --- a/packages/cli/src/sl.test.ts +++ b/packages/cli/src/sl.test.ts @@ -190,7 +190,7 @@ joins: [] it('runs sl query and prints SQL output', async () => { const projectDir = join(tempDir, 'project'); const project = await initKtxProject({ projectDir, projectName: 'warehouse' }); - project.config.connections.warehouse = { driver: 'postgres', readonly: true }; + project.config.connections.warehouse = { driver: 'postgres' }; await project.fileStore.writeFile( 'semantic-layer/warehouse/orders.yaml', `name: orders @@ -247,7 +247,7 @@ joins: [] it('runs sl query from a JSON query file', async () => { const projectDir = join(tempDir, 'project'); const project = await initKtxProject({ projectDir, projectName: 'warehouse' }); - project.config.connections.warehouse = { driver: 'postgres', readonly: true }; + project.config.connections.warehouse = { driver: 'postgres' }; await project.fileStore.writeFile( 'semantic-layer/warehouse/orders.yaml', `name: orders @@ -314,7 +314,7 @@ joins: [] it('creates default sl query compute through the managed runtime helper', async () => { const projectDir = join(tempDir, 'project'); const project = await initKtxProject({ projectDir, projectName: 'warehouse' }); - project.config.connections.warehouse = { driver: 'postgres', readonly: true }; + project.config.connections.warehouse = { driver: 'postgres' }; await project.fileStore.writeFile( 'semantic-layer/warehouse/orders.yaml', `name: orders @@ -375,7 +375,7 @@ joins: [] it('executes sl query through the injected query executor', async () => { const projectDir = join(tempDir, 'project'); const project = await initKtxProject({ projectDir, projectName: 'warehouse' }); - project.config.connections.warehouse = { driver: 'postgres', url: 'postgres://example/db', readonly: true }; + project.config.connections.warehouse = { driver: 'postgres', url: 'postgres://example/db' }; await project.fileStore.writeFile( 'semantic-layer/warehouse/orders.yaml', `name: orders @@ -471,7 +471,7 @@ joins: [] `); db.close(); - project.config.connections.warehouse = { driver: 'sqlite', path: 'warehouse.db', readonly: true }; + project.config.connections.warehouse = { driver: 'sqlite', path: 'warehouse.db' }; await writeFile( join(projectDir, 'ktx.yaml'), [ @@ -480,7 +480,6 @@ joins: [] ' warehouse:', ' driver: sqlite', ' path: warehouse.db', - ' readonly: true', '', ].join('\n'), 'utf-8', diff --git a/packages/cli/src/standalone-smoke.test.ts b/packages/cli/src/standalone-smoke.test.ts index c6fefd96..54eaae59 100644 --- a/packages/cli/src/standalone-smoke.test.ts +++ b/packages/cli/src/standalone-smoke.test.ts @@ -106,7 +106,6 @@ async function writeSqliteScanConfig(projectDir: string, dbPath: string, enrich ' warehouse:', ' driver: sqlite', ` path: ${JSON.stringify(dbPath)}`, - ' readonly: true', 'ingest:', ' adapters:', ' - live-database', diff --git a/packages/connector-bigquery/src/connector.test.ts b/packages/connector-bigquery/src/connector.test.ts index a1c23864..46dc3b53 100644 --- a/packages/connector-bigquery/src/connector.test.ts +++ b/packages/connector-bigquery/src/connector.test.ts @@ -100,7 +100,6 @@ const connection = { dataset_id: 'analytics', credentials_json: JSON.stringify({ project_id: 'project-1', client_email: 'reader@example.test' }), location: 'US', - readonly: true, }; describe('KtxBigQueryScanConnector', () => { @@ -112,12 +111,6 @@ describe('KtxBigQueryScanConnector', () => { datasetIds: ['analytics'], location: 'US', }); - expect(() => - bigQueryConnectionConfigFromConfig({ - connectionId: 'warehouse', - connection: { ...connection, readonly: false }, - }), - ).toThrow('Native BigQuery connector requires connections.warehouse.readonly: true'); }); it('introspects datasets, table metadata, primary keys, and normalized types', async () => { diff --git a/packages/connector-bigquery/src/connector.ts b/packages/connector-bigquery/src/connector.ts index a994912e..72cb8129 100644 --- a/packages/connector-bigquery/src/connector.ts +++ b/packages/connector-bigquery/src/connector.ts @@ -30,7 +30,6 @@ export interface KtxBigQueryConnectionConfig { dataset_ids?: string[]; credentials_json?: string; location?: string; - readonly?: boolean; [key: string]: unknown; } @@ -194,7 +193,9 @@ function normalizeValue(value: unknown): unknown { return value; } -export function isKtxBigQueryConnectionConfig(connection: KtxBigQueryConnectionConfig | undefined): boolean { +export function isKtxBigQueryConnectionConfig( + connection: KtxBigQueryConnectionConfig | undefined, +): connection is KtxBigQueryConnectionConfig { return String(connection?.driver ?? '').toLowerCase() === 'bigquery'; } @@ -203,11 +204,9 @@ export function bigQueryConnectionConfigFromConfig(input: { connection: KtxBigQueryConnectionConfig | undefined; env?: NodeJS.ProcessEnv; }): KtxBigQueryResolvedConnectionConfig { + const inputDriver = input.connection?.driver ?? 'unknown'; if (!isKtxBigQueryConnectionConfig(input.connection)) { - throw new Error(`Native BigQuery connector cannot run driver "${input.connection?.driver ?? 'unknown'}"`); - } - if (input.connection?.readonly !== true) { - throw new Error(`Native BigQuery connector requires connections.${input.connectionId}.readonly: true`); + throw new Error(`Native BigQuery connector cannot run driver "${inputDriver}"`); } const env = input.env ?? process.env; diff --git a/packages/connector-clickhouse/src/connector.test.ts b/packages/connector-clickhouse/src/connector.test.ts index 7ed60efa..4f8b7f52 100644 --- a/packages/connector-clickhouse/src/connector.test.ts +++ b/packages/connector-clickhouse/src/connector.test.ts @@ -112,7 +112,6 @@ describe('KtxClickHouseScanConnector', () => { username: 'reader', password: 'test-pass', // pragma: allowlist secret ssl: true, - readonly: true, }, }), ).toMatchObject({ @@ -123,12 +122,6 @@ describe('KtxClickHouseScanConnector', () => { password: 'test-pass', // pragma: allowlist secret ssl: true, }); - expect(() => - clickHouseClientConfigFromConfig({ - connectionId: 'warehouse', - connection: { driver: 'clickhouse', host: 'ch.example.test', database: 'analytics', readonly: false }, - }), - ).toThrow('Native ClickHouse connector requires connections.warehouse.readonly: true'); }); it('introspects schema, primary keys, comments, row counts, and views', async () => { @@ -140,7 +133,6 @@ describe('KtxClickHouseScanConnector', () => { database: 'analytics', username: 'reader', password: 'test-pass', // pragma: allowlist secret - readonly: true, }, clientFactory: fakeClientFactory(), now: () => new Date('2026-04-29T14:00:00.000Z'), @@ -189,7 +181,6 @@ describe('KtxClickHouseScanConnector', () => { database: 'analytics', username: 'reader', password: 'test-pass', // pragma: allowlist secret - readonly: true, }, clientFactory, }); @@ -253,7 +244,6 @@ describe('KtxClickHouseScanConnector', () => { database: 'analytics', username: 'reader', password: 'test-pass', // pragma: allowlist secret - readonly: true, }, }, clientFactory: fakeClientFactory(), diff --git a/packages/connector-clickhouse/src/connector.ts b/packages/connector-clickhouse/src/connector.ts index 0273a62b..4b39c943 100644 --- a/packages/connector-clickhouse/src/connector.ts +++ b/packages/connector-clickhouse/src/connector.ts @@ -35,7 +35,6 @@ export interface KtxClickHouseConnectionConfig { password?: string; url?: string; ssl?: boolean; - readonly?: boolean; [key: string]: unknown; } @@ -193,7 +192,9 @@ function isNullableClickHouseType(type: string): boolean { return type.startsWith('Nullable(') || type.startsWith('LowCardinality(Nullable('); } -export function isKtxClickHouseConnectionConfig(connection: KtxClickHouseConnectionConfig | undefined): boolean { +export function isKtxClickHouseConnectionConfig( + connection: KtxClickHouseConnectionConfig | undefined, +): connection is KtxClickHouseConnectionConfig { return String(connection?.driver ?? '').toLowerCase() === 'clickhouse'; } @@ -202,11 +203,9 @@ export function clickHouseClientConfigFromConfig(input: { connection: KtxClickHouseConnectionConfig | undefined; env?: NodeJS.ProcessEnv; }): KtxClickHouseResolvedClientConfig { + const inputDriver = input.connection?.driver ?? 'unknown'; if (!isKtxClickHouseConnectionConfig(input.connection)) { - throw new Error(`Native ClickHouse connector cannot run driver "${input.connection?.driver ?? 'unknown'}"`); - } - if (input.connection?.readonly !== true) { - throw new Error(`Native ClickHouse connector requires connections.${input.connectionId}.readonly: true`); + throw new Error(`Native ClickHouse connector cannot run driver "${inputDriver}"`); } const env = input.env ?? process.env; diff --git a/packages/connector-mysql/src/connector.test.ts b/packages/connector-mysql/src/connector.test.ts index 3e7ac1e1..c5c5a3fa 100644 --- a/packages/connector-mysql/src/connector.test.ts +++ b/packages/connector-mysql/src/connector.test.ts @@ -92,7 +92,7 @@ function fakePoolFactory(): KtxMysqlPoolFactory { describe('KtxMysqlScanConnector', () => { it('resolves MySQL connection configuration safely', () => { - expect(isKtxMysqlConnectionConfig({ driver: 'mysql', host: 'localhost', database: 'analytics', readonly: true })).toBe(true); + expect(isKtxMysqlConnectionConfig({ driver: 'mysql', host: 'localhost', database: 'analytics' })).toBe(true); expect(isKtxMysqlConnectionConfig({ driver: 'postgres', host: 'localhost', database: 'analytics' })).toBe(false); expect( mysqlConnectionPoolConfigFromConfig({ @@ -105,7 +105,6 @@ describe('KtxMysqlScanConnector', () => { username: 'reader', password: 'secret', // pragma: allowlist secret ssl: true, - readonly: true, }, }), ).toMatchObject({ @@ -116,12 +115,6 @@ describe('KtxMysqlScanConnector', () => { password: 'secret', // pragma: allowlist secret ssl: { rejectUnauthorized: false }, }); - expect(() => - mysqlConnectionPoolConfigFromConfig({ - connectionId: 'warehouse', - connection: { driver: 'mysql', host: 'db.example.test', database: 'analytics', readonly: false }, - }), - ).toThrow('Native MySQL connector requires connections.warehouse.readonly: true'); }); it('introspects schema, primary keys, comments, row counts, views, and foreign keys', async () => { @@ -133,7 +126,6 @@ describe('KtxMysqlScanConnector', () => { database: 'analytics', username: 'reader', password: 'secret', // pragma: allowlist secret - readonly: true, }, poolFactory: fakePoolFactory(), now: () => new Date('2026-04-29T12:00:00.000Z'), @@ -192,7 +184,6 @@ describe('KtxMysqlScanConnector', () => { database: 'analytics', username: 'reader', password: 'secret', // pragma: allowlist secret - readonly: true, }, poolFactory, }); @@ -249,7 +240,6 @@ describe('KtxMysqlScanConnector', () => { database: 'analytics', username: 'reader', password: 'secret', // pragma: allowlist secret - readonly: true, }, }, poolFactory: fakePoolFactory(), diff --git a/packages/connector-mysql/src/connector.ts b/packages/connector-mysql/src/connector.ts index 69a09272..62bb1880 100644 --- a/packages/connector-mysql/src/connector.ts +++ b/packages/connector-mysql/src/connector.ts @@ -35,7 +35,6 @@ export interface KtxMysqlConnectionConfig { password?: string; url?: string; ssl?: boolean | { rejectUnauthorized?: boolean }; - readonly?: boolean; [key: string]: unknown; } @@ -232,7 +231,9 @@ function queryParams(params: Record | unknown[] | undefined): u return Array.isArray(params) ? params : Object.values(params); } -export function isKtxMysqlConnectionConfig(connection: KtxMysqlConnectionConfig | undefined): boolean { +export function isKtxMysqlConnectionConfig( + connection: KtxMysqlConnectionConfig | undefined, +): connection is KtxMysqlConnectionConfig { return String(connection?.driver ?? '').toLowerCase() === 'mysql'; } @@ -241,11 +242,9 @@ export function mysqlConnectionPoolConfigFromConfig(input: { connection: KtxMysqlConnectionConfig | undefined; env?: NodeJS.ProcessEnv; }): KtxMysqlPoolConfig { + const inputDriver = input.connection?.driver ?? 'unknown'; if (!isKtxMysqlConnectionConfig(input.connection)) { - throw new Error(`Native MySQL connector cannot run driver "${input.connection?.driver ?? 'unknown'}"`); - } - if (input.connection?.readonly !== true) { - throw new Error(`Native MySQL connector requires connections.${input.connectionId}.readonly: true`); + throw new Error(`Native MySQL connector cannot run driver "${inputDriver}"`); } const env = input.env ?? process.env; diff --git a/packages/connector-postgres/src/connector.test.ts b/packages/connector-postgres/src/connector.test.ts index 96443c90..8093acda 100644 --- a/packages/connector-postgres/src/connector.test.ts +++ b/packages/connector-postgres/src/connector.test.ts @@ -102,7 +102,7 @@ function metadataResults(): Map { describe('KtxPostgresScanConnector', () => { it('resolves configuration safely', () => { - expect(isKtxPostgresConnectionConfig({ driver: 'postgres', url: 'env:DATABASE_URL', readonly: true })).toBe(true); + expect(isKtxPostgresConnectionConfig({ driver: 'postgres', url: 'env:DATABASE_URL' })).toBe(true); expect(isKtxPostgresConnectionConfig({ driver: 'postgresql', host: 'db', database: 'analytics' })).toBe(true); expect(isKtxPostgresConnectionConfig({ driver: 'mysql', host: 'db' })).toBe(false); expect( @@ -115,7 +115,6 @@ describe('KtxPostgresScanConnector', () => { username: 'reader', password: 'test-password', // pragma: allowlist secret schemas: ['analytics', 'public'], - readonly: true, ssl: true, rejectUnauthorized: false, }, @@ -134,7 +133,6 @@ describe('KtxPostgresScanConnector', () => { connection: { driver: 'postgres', url: 'env:DEMO_DATABASE_URL', - readonly: true, }, env: { DEMO_DATABASE_URL: 'postgresql://reader@demo.example.test:5432/demo?sslmode=prefer', @@ -148,12 +146,16 @@ describe('KtxPostgresScanConnector', () => { }); expect(libpqPreferConfig).not.toHaveProperty('connectionString'); expect(libpqPreferConfig).not.toHaveProperty('ssl'); - expect(() => + expect( postgresPoolConfigFromConfig({ connectionId: 'warehouse', connection: { driver: 'postgres', host: 'db.example.test', database: 'analytics', username: 'reader' }, }), - ).toThrow('Native PostgreSQL connector requires connections.warehouse.readonly: true'); + ).toMatchObject({ + host: 'db.example.test', + database: 'analytics', + user: 'reader', + }); }); it('introspects schemas, tables, views, primary keys, comments, row counts, and foreign keys', async () => { @@ -166,7 +168,6 @@ describe('KtxPostgresScanConnector', () => { username: 'reader', password: 'test-password', // pragma: allowlist secret schema: 'public', - readonly: true, }, poolFactory: fakePoolFactory(metadataResults()), now: () => new Date('2026-04-29T10:00:00.000Z'), @@ -225,7 +226,6 @@ describe('KtxPostgresScanConnector', () => { username: 'reader', password: 'test-password', // pragma: allowlist secret schema: 'public', - readonly: true, }, poolFactory: fakePoolFactory(metadataResults()), }); @@ -274,7 +274,6 @@ describe('KtxPostgresScanConnector', () => { username: 'reader', password: 'test-password', // pragma: allowlist secret schema: 'public', - readonly: true, }, }, poolFactory: fakePoolFactory(metadataResults()), @@ -347,7 +346,6 @@ describe('KtxPostgresScanConnector', () => { username: 'reader', password: 'test-password', // pragma: allowlist secret schema: 'public', - readonly: true, }, }, poolFactory: endAwarePoolFactory, @@ -383,7 +381,6 @@ describe('KtxPostgresScanConnector', () => { database: 'analytics', username: 'reader', password: 'test-password', // pragma: allowlist secret - readonly: true, }, poolFactory, }); diff --git a/packages/connector-postgres/src/connector.ts b/packages/connector-postgres/src/connector.ts index 65490040..7f5ed65c 100644 --- a/packages/connector-postgres/src/connector.ts +++ b/packages/connector-postgres/src/connector.ts @@ -61,7 +61,6 @@ export interface KtxPostgresConnectionConfig { sslmode?: string; sslMode?: string; rejectUnauthorized?: boolean; - readonly?: boolean; [key: string]: unknown; } @@ -291,7 +290,9 @@ function searchPathSchemasFromConnection(connection: KtxPostgresConnectionConfig return schemas.includes('public') ? schemas : [...schemas, 'public']; } -export function isKtxPostgresConnectionConfig(connection: KtxPostgresConnectionConfig | undefined): boolean { +export function isKtxPostgresConnectionConfig( + connection: KtxPostgresConnectionConfig | undefined, +): connection is KtxPostgresConnectionConfig { const driver = String(connection?.driver ?? '').toLowerCase(); return driver === 'postgres' || driver === 'postgresql'; } @@ -301,11 +302,9 @@ export function postgresPoolConfigFromConfig(input: { connection: KtxPostgresConnectionConfig | undefined; env?: NodeJS.ProcessEnv; }): KtxPostgresPoolConfig { + const inputDriver = input.connection?.driver ?? 'unknown'; if (!isKtxPostgresConnectionConfig(input.connection)) { - throw new Error(`Native PostgreSQL connector cannot run driver "${input.connection?.driver ?? 'unknown'}"`); - } - if (input.connection?.readonly !== true) { - throw new Error(`Native PostgreSQL connector requires connections.${input.connectionId}.readonly: true`); + throw new Error(`Native PostgreSQL connector cannot run driver "${inputDriver}"`); } const env = input.env ?? process.env; diff --git a/packages/connector-postgres/src/historic-sql-query-client.test.ts b/packages/connector-postgres/src/historic-sql-query-client.test.ts index f02cb9c3..b9c9fd40 100644 --- a/packages/connector-postgres/src/historic-sql-query-client.test.ts +++ b/packages/connector-postgres/src/historic-sql-query-client.test.ts @@ -30,7 +30,6 @@ describe('KtxPostgresHistoricSqlQueryClient', () => { connectionId: 'warehouse', connection: { driver: 'postgres', - readonly: true, url: 'postgresql://readonly:secret@pg.example.test/warehouse', // pragma: allowlist secret }, poolFactory, diff --git a/packages/connector-snowflake/src/connector.test.ts b/packages/connector-snowflake/src/connector.test.ts index 91bb33d4..a49be885 100644 --- a/packages/connector-snowflake/src/connector.test.ts +++ b/packages/connector-snowflake/src/connector.test.ts @@ -78,7 +78,6 @@ describe('KtxSnowflakeScanConnector', () => { warehouse: 'WH', database: 'ANALYTICS', username: 'reader', - readonly: true, }), ).toBe(true); expect(isKtxSnowflakeConnectionConfig({ driver: 'bigquery' })).toBe(false); @@ -94,7 +93,6 @@ describe('KtxSnowflakeScanConnector', () => { schema_name: 'PUBLIC', username: 'reader', password: 'fixture-pass', // pragma: allowlist secret - readonly: true, }, }), ).toMatchObject({ @@ -105,12 +103,6 @@ describe('KtxSnowflakeScanConnector', () => { username: 'reader', authMethod: 'password', }); - expect(() => - snowflakeConnectionConfigFromConfig({ - connectionId: 'warehouse', - connection: { driver: 'snowflake', account: 'acct', readonly: false }, - }), - ).toThrow('Native Snowflake connector requires connections.warehouse.readonly: true'); }); it('introspects schema, primary keys, comments, row counts, and dimensions', async () => { @@ -125,7 +117,6 @@ describe('KtxSnowflakeScanConnector', () => { schema_name: 'PUBLIC', username: 'reader', password: 'fixture-pass', // pragma: allowlist secret - readonly: true, }, driverFactory: fakeDriverFactory(), now: () => new Date('2026-04-29T18:00:00.000Z'), @@ -185,7 +176,6 @@ describe('KtxSnowflakeScanConnector', () => { schema_name: 'PUBLIC', username: 'reader', password: 'fixture-pass', // pragma: allowlist secret - readonly: true, }, driverFactory, }); @@ -243,7 +233,6 @@ describe('KtxSnowflakeScanConnector', () => { schema_name: 'PUBLIC', username: 'reader', password: 'fixture-pass', // pragma: allowlist secret - readonly: true, }, }, driverFactory: fakeDriverFactory(), diff --git a/packages/connector-snowflake/src/connector.ts b/packages/connector-snowflake/src/connector.ts index 063976f7..76fc34fd 100644 --- a/packages/connector-snowflake/src/connector.ts +++ b/packages/connector-snowflake/src/connector.ts @@ -38,7 +38,6 @@ export interface KtxSnowflakeConnectionConfig { privateKey?: string; passphrase?: string; role?: string; - readonly?: boolean; [key: string]: unknown; } @@ -191,7 +190,9 @@ function toSnowflakeBinds(params: unknown[] | undefined): snowflake.Binds | unde return params?.map((value) => toSnowflakeBind(value)); } -export function isKtxSnowflakeConnectionConfig(connection: KtxSnowflakeConnectionConfig | undefined): boolean { +export function isKtxSnowflakeConnectionConfig( + connection: KtxSnowflakeConnectionConfig | undefined, +): connection is KtxSnowflakeConnectionConfig { return String(connection?.driver ?? '').toLowerCase() === 'snowflake'; } @@ -200,11 +201,9 @@ export function snowflakeConnectionConfigFromConfig(input: { connection: KtxSnowflakeConnectionConfig | undefined; env?: NodeJS.ProcessEnv; }): KtxSnowflakeResolvedConnectionConfig { + const inputDriver = input.connection?.driver ?? 'unknown'; if (!isKtxSnowflakeConnectionConfig(input.connection)) { - throw new Error(`Native Snowflake connector cannot run driver "${input.connection?.driver ?? 'unknown'}"`); - } - if (input.connection?.readonly !== true) { - throw new Error(`Native Snowflake connector requires connections.${input.connectionId}.readonly: true`); + throw new Error(`Native Snowflake connector cannot run driver "${inputDriver}"`); } const env = input.env ?? process.env; const authMethod = input.connection?.authMethod ?? 'password'; @@ -395,7 +394,7 @@ class SnowflakeSdkDriver implements KtxSnowflakeDriver { private async createConnection(): Promise { const patch = await this.sdkOptionsProvider?.resolve({ account: this.resolved.account, - connection: { ...this.resolved, driver: 'snowflake', readonly: true }, + connection: { ...this.resolved, driver: 'snowflake' }, }); if (patch?.close) { this.closeSdkOptions.push(patch.close); diff --git a/packages/connector-sqlite/src/connector.test.ts b/packages/connector-sqlite/src/connector.test.ts index 4bc26ec9..9bee5567 100644 --- a/packages/connector-sqlite/src/connector.test.ts +++ b/packages/connector-sqlite/src/connector.test.ts @@ -53,45 +53,43 @@ describe('KtxSqliteScanConnector', () => { writeFileSync(pointerPath, dbPath, 'utf-8'); try { - expect(isKtxSqliteConnectionConfig({ driver: 'sqlite', path: 'warehouse.db', readonly: true })).toBe(true); - expect(isKtxSqliteConnectionConfig({ driver: 'postgres', url: 'env:DATABASE_URL', readonly: true })).toBe( - false, - ); + expect(isKtxSqliteConnectionConfig({ driver: 'sqlite', path: 'warehouse.db' })).toBe(true); + expect(isKtxSqliteConnectionConfig({ driver: 'postgres', url: 'env:DATABASE_URL' })).toBe(false); expect( sqliteDatabasePathFromConfig({ connectionId: 'warehouse', projectDir: tempDir, - connection: { driver: 'sqlite', path: 'warehouse.db', readonly: true }, + connection: { driver: 'sqlite', path: 'warehouse.db' }, }), ).toBe(dbPath); expect( sqliteDatabasePathFromConfig({ connectionId: 'warehouse', projectDir: tempDir, - connection: { driver: 'sqlite', url: 'env:KTX_SQLITE_TEST_URL', readonly: true }, + connection: { driver: 'sqlite', url: 'env:KTX_SQLITE_TEST_URL' }, }), ).toBe(dbPath); expect( sqliteDatabasePathFromConfig({ connectionId: 'warehouse', projectDir: tempDir, - connection: { driver: 'sqlite', url: `file://${dbPath}`, readonly: true }, + connection: { driver: 'sqlite', url: `file://${dbPath}` }, }), ).toBe(dbPath); expect( sqliteDatabasePathFromConfig({ connectionId: 'warehouse', projectDir: tempDir, - connection: { driver: 'sqlite', path: `file:${pointerPath}`, readonly: true }, + connection: { driver: 'sqlite', path: `file:${pointerPath}` }, }), ).toBe(dbPath); - expect(() => + expect( sqliteDatabasePathFromConfig({ connectionId: 'warehouse', projectDir: tempDir, - connection: { driver: 'sqlite', path: 'warehouse.db', readonly: false }, + connection: { driver: 'sqlite', path: 'warehouse.db' }, }), - ).toThrow('Native SQLite connector requires connections.warehouse.readonly: true'); + ).toBe(dbPath); } finally { if (originalDatabaseUrl === undefined) { delete process.env.KTX_SQLITE_TEST_URL; @@ -104,7 +102,7 @@ describe('KtxSqliteScanConnector', () => { it('introspects schema, primary keys, row counts, views, and foreign keys', async () => { const connector = new KtxSqliteScanConnector({ connectionId: 'warehouse', - connection: { driver: 'sqlite', path: dbPath, readonly: true }, + connection: { driver: 'sqlite', path: dbPath }, now: () => new Date('2026-04-29T10:00:00.000Z'), }); @@ -151,7 +149,7 @@ describe('KtxSqliteScanConnector', () => { it('runs samples, distinct values, statistics, and read-only SQL', async () => { const connector = new KtxSqliteScanConnector({ connectionId: 'warehouse', - connection: { driver: 'sqlite', path: dbPath, readonly: true }, + connection: { driver: 'sqlite', path: dbPath }, }); await expect( @@ -199,7 +197,7 @@ describe('KtxSqliteScanConnector', () => { const introspection = createSqliteLiveDatabaseIntrospection({ projectDir: tempDir, connections: { - warehouse: { driver: 'sqlite', path: 'warehouse.db', readonly: true }, + warehouse: { driver: 'sqlite', path: 'warehouse.db' }, }, now: () => new Date('2026-04-29T10:00:00.000Z'), }); diff --git a/packages/connector-sqlite/src/connector.ts b/packages/connector-sqlite/src/connector.ts index c42db002..2a0d997e 100644 --- a/packages/connector-sqlite/src/connector.ts +++ b/packages/connector-sqlite/src/connector.ts @@ -29,7 +29,6 @@ export interface KtxSqliteConnectionConfig { path?: string; url?: string; file_path?: string; - readonly?: boolean; [key: string]: unknown; } @@ -135,17 +134,17 @@ function stripLeadingSqlComments(sql: string): string { return sql.slice(index); } -export function isKtxSqliteConnectionConfig(connection: KtxSqliteConnectionConfig | undefined): boolean { +export function isKtxSqliteConnectionConfig( + connection: KtxSqliteConnectionConfig | undefined, +): connection is KtxSqliteConnectionConfig { const driver = String(connection?.driver ?? '').toLowerCase(); return driver === 'sqlite' || driver === 'sqlite3'; } export function sqliteDatabasePathFromConfig(input: SqliteDatabasePathInput): string { + const inputDriver = input.connection?.driver ?? 'unknown'; if (!isKtxSqliteConnectionConfig(input.connection)) { - throw new Error(`Native SQLite connector cannot run driver "${input.connection?.driver ?? 'unknown'}"`); - } - if (input.connection?.readonly !== true) { - throw new Error(`Native SQLite connector requires connections.${input.connectionId}.readonly: true`); + throw new Error(`Native SQLite connector cannot run driver "${inputDriver}"`); } const configuredPath = stringConfigValue(input.connection, 'path') ?? diff --git a/packages/connector-sqlserver/src/connector.test.ts b/packages/connector-sqlserver/src/connector.test.ts index eebab0ba..b7915fa8 100644 --- a/packages/connector-sqlserver/src/connector.test.ts +++ b/packages/connector-sqlserver/src/connector.test.ts @@ -145,7 +145,6 @@ describe('KtxSqlServerScanConnector', () => { driver: 'sqlserver', host: 'localhost', database: 'analytics', - readonly: true, }), ).toBe(true); expect(isKtxSqlServerConnectionConfig({ driver: 'mysql', host: 'localhost', database: 'analytics' })).toBe(false); @@ -159,7 +158,6 @@ describe('KtxSqlServerScanConnector', () => { database: 'analytics', username: 'reader', trustServerCertificate: false, - readonly: true, }, }), ).toMatchObject({ @@ -169,12 +167,6 @@ describe('KtxSqlServerScanConnector', () => { user: 'reader', options: { encrypt: true, trustServerCertificate: false }, }); - expect(() => - sqlServerConnectionPoolConfigFromConfig({ - connectionId: 'warehouse', - connection: { driver: 'sqlserver', host: 'db.example.test', database: 'analytics', readonly: false }, - }), - ).toThrow('Native SQL Server connector requires connections.warehouse.readonly: true'); }); it('introspects schema, primary keys, comments, row counts, views, and foreign keys', async () => { @@ -186,7 +178,6 @@ describe('KtxSqlServerScanConnector', () => { database: 'analytics', username: 'reader', schema: 'dbo', - readonly: true, }, poolFactory: fakePoolFactory(), now: () => new Date('2026-04-29T16:00:00.000Z'), @@ -246,7 +237,6 @@ describe('KtxSqlServerScanConnector', () => { database: 'analytics', username: 'reader', schema: 'dbo', - readonly: true, }, poolFactory, }); @@ -315,7 +305,6 @@ describe('KtxSqlServerScanConnector', () => { database: 'analytics', username: 'reader', schema: 'dbo', - readonly: true, }, }, poolFactory: fakePoolFactory(), diff --git a/packages/connector-sqlserver/src/connector.ts b/packages/connector-sqlserver/src/connector.ts index 189ff98b..73a46aab 100644 --- a/packages/connector-sqlserver/src/connector.ts +++ b/packages/connector-sqlserver/src/connector.ts @@ -37,7 +37,6 @@ export interface KtxSqlServerConnectionConfig { schema?: string; schemas?: string[]; trustServerCertificate?: boolean; - readonly?: boolean; [key: string]: unknown; } @@ -234,7 +233,9 @@ function limitSqlForSqlServerExecution(sqlText: string, maxRows: number | undefi return `SELECT TOP ${maxRows} * FROM (${trimmed}) AS ktx_query_result`; } -export function isKtxSqlServerConnectionConfig(connection: KtxSqlServerConnectionConfig | undefined): boolean { +export function isKtxSqlServerConnectionConfig( + connection: KtxSqlServerConnectionConfig | undefined, +): connection is KtxSqlServerConnectionConfig { return String(connection?.driver ?? '').toLowerCase() === 'sqlserver'; } @@ -243,11 +244,9 @@ export function sqlServerConnectionPoolConfigFromConfig(input: { connection: KtxSqlServerConnectionConfig | undefined; env?: NodeJS.ProcessEnv; }): KtxSqlServerPoolConfig { + const inputDriver = input.connection?.driver ?? 'unknown'; if (!isKtxSqlServerConnectionConfig(input.connection)) { - throw new Error(`Native SQL Server connector cannot run driver "${input.connection?.driver ?? 'unknown'}"`); - } - if (input.connection?.readonly !== true) { - throw new Error(`Native SQL Server connector requires connections.${input.connectionId}.readonly: true`); + throw new Error(`Native SQL Server connector cannot run driver "${inputDriver}"`); } const env = input.env ?? process.env; diff --git a/packages/context/src/connections/local-query-executor.test.ts b/packages/context/src/connections/local-query-executor.test.ts index fd94c6dc..d2f77975 100644 --- a/packages/context/src/connections/local-query-executor.test.ts +++ b/packages/context/src/connections/local-query-executor.test.ts @@ -26,14 +26,14 @@ describe('createDefaultLocalQueryExecutor', () => { await expect( executor.execute({ connectionId: 'pg', - connection: { driver: 'postgres', readonly: true }, + connection: { driver: 'postgres' }, sql: 'select 1', }), ).resolves.toMatchObject({ headers: ['pg'] }); await expect( executor.execute({ connectionId: 'local', - connection: { driver: 'sqlite', readonly: true }, + connection: { driver: 'sqlite' }, sql: 'select 1', }), ).resolves.toMatchObject({ headers: ['sqlite'] }); @@ -51,7 +51,7 @@ describe('createDefaultLocalQueryExecutor', () => { await expect( executor.execute({ connectionId: 'warehouse', - connection: { driver: 'snowflake', readonly: true }, + connection: { driver: 'snowflake' }, sql: 'select 1', }), ).rejects.toThrow('No local query executor is configured for driver "snowflake".'); diff --git a/packages/context/src/connections/postgres-query-executor.test.ts b/packages/context/src/connections/postgres-query-executor.test.ts index 2c52d97e..6bb522cf 100644 --- a/packages/context/src/connections/postgres-query-executor.test.ts +++ b/packages/context/src/connections/postgres-query-executor.test.ts @@ -37,7 +37,7 @@ describe('createPostgresQueryExecutor', () => { const result = await executor.execute({ connectionId: 'warehouse', - connection: { driver: 'postgres', url: 'postgres://example/db', readonly: true }, + connection: { driver: 'postgres', url: 'postgres://example/db' }, sql: 'select status, count(*) as order_count from public.orders group by status', maxRows: 50, }); @@ -80,7 +80,7 @@ describe('createPostgresQueryExecutor', () => { await expect( executor.execute({ connectionId: 'warehouse', - connection: { driver: 'postgres', url: 'postgres://example/db', readonly: true }, + connection: { driver: 'postgres', url: 'postgres://example/db' }, sql: 'select * from broken', maxRows: 10, }), @@ -89,23 +89,15 @@ describe('createPostgresQueryExecutor', () => { expect(client.end).toHaveBeenCalledTimes(1); }); - it('requires a Postgres url and read-only connection config', async () => { + it('requires a Postgres url', async () => { const executor = createPostgresQueryExecutor({ clientFactory: vi.fn() }); await expect( executor.execute({ connectionId: 'warehouse', - connection: { driver: 'postgres', readonly: true }, + connection: { driver: 'postgres' }, sql: 'select 1', }), ).rejects.toThrow('Local Postgres execution requires connections.warehouse.url'); - - await expect( - executor.execute({ - connectionId: 'warehouse', - connection: { driver: 'postgres', url: 'postgres://example/db', readonly: false }, - sql: 'select 1', - }), - ).rejects.toThrow('Local query execution requires connections.warehouse.readonly: true'); }); }); diff --git a/packages/context/src/connections/postgres-query-executor.ts b/packages/context/src/connections/postgres-query-executor.ts index 2ab142a5..b5f2d02e 100644 --- a/packages/context/src/connections/postgres-query-executor.ts +++ b/packages/context/src/connections/postgres-query-executor.ts @@ -37,18 +37,16 @@ export function createPostgresQueryExecutor(options: PostgresQueryExecutorOption return { async execute(input: KtxSqlQueryExecutionInput): Promise { const driver = connectionDriver(input); + const connection = input.connection; if (driver !== 'postgres' && driver !== 'postgresql') { - throw new Error(`Local Postgres execution cannot run driver "${input.connection?.driver ?? 'unknown'}".`); + throw new Error(`Local Postgres execution cannot run driver "${connection?.driver ?? 'unknown'}".`); } - if (input.connection?.readonly !== true) { - throw new Error(`Local query execution requires connections.${input.connectionId}.readonly: true.`); - } - if (typeof input.connection.url !== 'string' || input.connection.url.trim().length === 0) { + if (typeof connection?.url !== 'string' || connection.url.trim().length === 0) { throw new Error(`Local Postgres execution requires connections.${input.connectionId}.url.`); } const client = clientFactory({ - connectionString: input.connection.url, + connectionString: connection.url, statement_timeout: options.statementTimeoutMs ?? 30_000, query_timeout: options.queryTimeoutMs ?? 35_000, connectionTimeoutMillis: options.connectionTimeoutMs ?? 5_000, diff --git a/packages/context/src/connections/sqlite-query-executor.test.ts b/packages/context/src/connections/sqlite-query-executor.test.ts index 3046f9bb..facb5139 100644 --- a/packages/context/src/connections/sqlite-query-executor.test.ts +++ b/packages/context/src/connections/sqlite-query-executor.test.ts @@ -38,7 +38,7 @@ describe('createSqliteQueryExecutor', () => { const result = await executor.execute({ connectionId: 'warehouse', projectDir: tempDir, - connection: { driver: 'sqlite', path: 'warehouse.db', readonly: true }, + connection: { driver: 'sqlite', path: 'warehouse.db' }, sql: 'select status, count(*) as order_count from orders group by status order by status', maxRows: 10, }); @@ -60,7 +60,7 @@ describe('createSqliteQueryExecutor', () => { sqliteDatabasePathFromConnection({ connectionId: 'warehouse', projectDir: tempDir, - connection: { driver: 'sqlite', url: `file://${dbPath}`, readonly: true }, + connection: { driver: 'sqlite', url: `file://${dbPath}` }, sql: 'select 1', }), ).toBe(dbPath); @@ -74,7 +74,7 @@ describe('createSqliteQueryExecutor', () => { sqliteDatabasePathFromConnection({ connectionId: 'warehouse', projectDir: tempDir, - connection: { driver: 'sqlite', path: `file:${pointerPath}`, readonly: true }, + connection: { driver: 'sqlite', path: `file:${pointerPath}` }, sql: 'select 1', }), ).toBe(dbPath); @@ -89,7 +89,7 @@ describe('createSqliteQueryExecutor', () => { sqliteDatabasePathFromConnection({ connectionId: 'warehouse', projectDir: tempDir, - connection: { driver: 'sqlite', url: 'env:KTX_SQLITE_TEST_URL', readonly: true }, + connection: { driver: 'sqlite', url: 'env:KTX_SQLITE_TEST_URL' }, sql: 'select 1', }), ).toBe(dbPath); @@ -109,20 +109,20 @@ describe('createSqliteQueryExecutor', () => { executor.execute({ connectionId: 'warehouse', projectDir: tempDir, - connection: { driver: 'sqlite', path: 'warehouse.db', readonly: true }, + connection: { driver: 'sqlite', path: 'warehouse.db' }, sql: 'delete from orders', }), ).rejects.toThrow('Only read-only SELECT/WITH queries can be executed locally'); }); - it('requires a SQLite driver, read-only config, and a database path', async () => { + it('requires a SQLite driver and a database path', async () => { const executor = createSqliteQueryExecutor(); await expect( executor.execute({ connectionId: 'warehouse', projectDir: tempDir, - connection: { driver: 'postgres', path: 'warehouse.db', readonly: true }, + connection: { driver: 'postgres', path: 'warehouse.db' }, sql: 'select 1', }), ).rejects.toThrow('Local SQLite execution cannot run driver "postgres"'); @@ -131,16 +131,7 @@ describe('createSqliteQueryExecutor', () => { executor.execute({ connectionId: 'warehouse', projectDir: tempDir, - connection: { driver: 'sqlite', path: 'warehouse.db', readonly: false }, - sql: 'select 1', - }), - ).rejects.toThrow('Local query execution requires connections.warehouse.readonly: true'); - - await expect( - executor.execute({ - connectionId: 'warehouse', - projectDir: tempDir, - connection: { driver: 'sqlite', readonly: true }, + connection: { driver: 'sqlite' }, sql: 'select 1', }), ).rejects.toThrow('Local SQLite execution requires connections.warehouse.path or connections.warehouse.url'); diff --git a/packages/context/src/connections/sqlite-query-executor.ts b/packages/context/src/connections/sqlite-query-executor.ts index d32a37ba..2a87ef7d 100644 --- a/packages/context/src/connections/sqlite-query-executor.ts +++ b/packages/context/src/connections/sqlite-query-executor.ts @@ -54,9 +54,6 @@ export function sqliteDatabasePathFromConnection(input: KtxSqlQueryExecutionInpu if (driver !== 'sqlite' && driver !== 'sqlite3') { throw new Error(`Local SQLite execution cannot run driver "${input.connection?.driver ?? 'unknown'}".`); } - if (input.connection?.readonly !== true) { - throw new Error(`Local query execution requires connections.${input.connectionId}.readonly: true.`); - } const pathValue = stringConfigValue(input.connection, 'path'); const urlValue = stringConfigValue(input.connection, 'url'); diff --git a/packages/context/src/ingest/adapters/live-database/daemon-introspection.test.ts b/packages/context/src/ingest/adapters/live-database/daemon-introspection.test.ts index fe65920e..93a9739d 100644 --- a/packages/context/src/ingest/adapters/live-database/daemon-introspection.test.ts +++ b/packages/context/src/ingest/adapters/live-database/daemon-introspection.test.ts @@ -45,7 +45,6 @@ describe('createDaemonLiveDatabaseIntrospection', () => { warehouse: { driver: 'postgres', url: 'postgres://localhost:5432/warehouse', - readonly: true, }, }, schemas: ['public'], @@ -157,7 +156,6 @@ describe('createDaemonLiveDatabaseIntrospection', () => { warehouse: { driver: 'postgresql', url: 'postgres://localhost:5432/warehouse', - readonly: true, }, }, baseUrl: `http://127.0.0.1:${address.port}`, @@ -186,20 +184,18 @@ describe('createDaemonLiveDatabaseIntrospection', () => { } }); - it('requires a configured read-only postgres connection with a url', async () => { + it('requires a configured postgres connection with a url', async () => { const introspection = createDaemonLiveDatabaseIntrospection({ connections: { warehouse: { driver: 'postgres', - url: 'postgres://localhost:5432/warehouse', - readonly: false, }, }, runJson: vi.fn(async () => daemonResponse), }); await expect(introspection.extractSchema('warehouse')).rejects.toThrow( - 'Local live-database ingest requires connections.warehouse.readonly: true.', + 'Local live-database ingest requires connections.warehouse.url.', ); }); @@ -210,7 +206,6 @@ describe('createDaemonLiveDatabaseIntrospection', () => { warehouse: { driver: 'snowflake', url: 'snowflake://example', - readonly: true, }, }, runJson, diff --git a/packages/context/src/ingest/adapters/live-database/daemon-introspection.ts b/packages/context/src/ingest/adapters/live-database/daemon-introspection.ts index 531c1a66..6c333385 100644 --- a/packages/context/src/ingest/adapters/live-database/daemon-introspection.ts +++ b/packages/context/src/ingest/adapters/live-database/daemon-introspection.ts @@ -162,9 +162,6 @@ function requirePostgresConnection( if (driver !== 'postgres') { throw new Error(`Local live-database ingest cannot run driver "${connection?.driver ?? 'unknown'}".`); } - if (connection?.readonly !== true) { - throw new Error(`Local live-database ingest requires connections.${connectionId}.readonly: true.`); - } if (typeof connection.url !== 'string' || connection.url.trim().length === 0) { throw new Error(`Local live-database ingest requires connections.${connectionId}.url.`); } diff --git a/packages/context/src/ingest/local-stage-ingest.test.ts b/packages/context/src/ingest/local-stage-ingest.test.ts index 5f0ee501..3a0beaa5 100644 --- a/packages/context/src/ingest/local-stage-ingest.test.ts +++ b/packages/context/src/ingest/local-stage-ingest.test.ts @@ -39,7 +39,6 @@ async function writeLiveDatabaseConfig(projectDir: string): Promise { ' warehouse:', ' driver: postgres', ' url: postgres://localhost:5432/warehouse', - ' readonly: true', 'ingest:', ' adapters:', ' - live-database', diff --git a/packages/context/src/mcp/local-project-ports.test.ts b/packages/context/src/mcp/local-project-ports.test.ts index b95e4ad1..fab2f076 100644 --- a/packages/context/src/mcp/local-project-ports.test.ts +++ b/packages/context/src/mcp/local-project-ports.test.ts @@ -75,7 +75,6 @@ describe('createLocalProjectMcpContextPorts', () => { project.config.connections.warehouse = { driver: 'postgres', url: 'env:DATABASE_URL', - readonly: true, }; const ports = createLocalProjectMcpContextPorts(project); @@ -89,7 +88,6 @@ describe('createLocalProjectMcpContextPorts', () => { project.config.connections.warehouse = { driver: 'postgres', url: 'env:DATABASE_URL', - readonly: true, }; const connector = testConnector(); const createConnector = vi.fn(async () => connector); @@ -125,7 +123,6 @@ describe('createLocalProjectMcpContextPorts', () => { const project = await initKtxProject({ projectDir: tempDir, projectName: 'warehouse' }); project.config.connections.warehouse = { driver: 'postgres', - readonly: true, }; project.config.ingest.adapters = ['fake']; project.config.ingest.embeddings = { @@ -633,7 +630,6 @@ describe('createLocalProjectMcpContextPorts', () => { project.config.connections.warehouse = { driver: 'postgres', url: 'env:DATABASE_URL', - readonly: true, }; const shapeOnlyPorts = createLocalProjectMcpContextPorts(project); await shapeOnlyPorts.semanticLayer?.writeSource({ @@ -720,7 +716,6 @@ describe('createLocalProjectMcpContextPorts', () => { project.config.connections.warehouse = { driver: 'postgres', url: 'env:DATABASE_URL', - readonly: true, }; const shapeOnlyPorts = createLocalProjectMcpContextPorts(project); await shapeOnlyPorts.semanticLayer?.writeSource({ @@ -958,7 +953,6 @@ describe('createLocalProjectMcpContextPorts', () => { project.config.connections.warehouse = { driver: 'postgres', url: 'postgres://localhost:5432/warehouse', - readonly: true, }; project.config.ingest.adapters = ['live-database']; project.config.llm = { @@ -1034,7 +1028,6 @@ describe('createLocalProjectMcpContextPorts', () => { project.config.connections.warehouse = { driver: 'postgres', url: 'env:DATABASE_URL', - readonly: true, }; project.config.ingest.adapters = ['live-database']; const ports = createLocalProjectMcpContextPorts(project, { diff --git a/packages/context/src/memory/local-memory.test.ts b/packages/context/src/memory/local-memory.test.ts index e44a5bf1..83b22146 100644 --- a/packages/context/src/memory/local-memory.test.ts +++ b/packages/context/src/memory/local-memory.test.ts @@ -145,7 +145,7 @@ describe('createLocalProjectMemoryCapture', () => { it('captures a semantic-layer source for a named local connection id', async () => { const project = await initKtxProject({ projectDir: tempDir, projectName: 'warehouse' }); - project.config.connections.warehouse = { driver: 'postgres', readonly: true }; + project.config.connections.warehouse = { driver: 'postgres' }; const agentRunner = { runLoop: async ({ toolSet, diff --git a/packages/context/src/project/config.ts b/packages/context/src/project/config.ts index ad5ecb8a..0c345473 100644 --- a/packages/context/src/project/config.ts +++ b/packages/context/src/project/config.ts @@ -69,7 +69,6 @@ export interface KtxProjectScanConfig { export interface KtxProjectConnectionConfig { driver: string; url?: string; - readonly?: boolean; [key: string]: unknown; } diff --git a/packages/context/src/scan/local-scan.test.ts b/packages/context/src/scan/local-scan.test.ts index 6c3e877f..581f02d7 100644 --- a/packages/context/src/scan/local-scan.test.ts +++ b/packages/context/src/scan/local-scan.test.ts @@ -110,7 +110,6 @@ async function writeLiveDatabaseConfig(projectDir: string): Promise { ' warehouse:', ' driver: postgres', ' url: env:DATABASE_URL', - ' readonly: true', 'ingest:', ' adapters:', ' - live-database', @@ -1006,7 +1005,6 @@ describe('local scan', () => { ' warehouse:', ' driver: postgres', ' url: env:DATABASE_URL', - ' readonly: true', 'ingest:', ' adapters:', ' - live-database', @@ -1363,7 +1361,6 @@ describe('local scan', () => { ' warehouse:', ' driver: sqlite', ' path: warehouse.db', - ' readonly: true', 'ingest:', ' adapters:', ' - live-database', @@ -1396,7 +1393,6 @@ describe('local scan', () => { ' warehouse:', ' driver: mysql', ' url: env:MYSQL_URL', - ' readonly: true', 'ingest:', ' adapters:', ' - live-database', @@ -1432,7 +1428,6 @@ describe('local scan', () => { ' database: analytics', ' username: reader', ' password: env:CLICKHOUSE_PASSWORD', - ' readonly: true', 'ingest:', ' adapters:', ' - live-database', @@ -1468,7 +1463,6 @@ describe('local scan', () => { ' database: analytics', ' username: reader', ' schema: dbo', - ' readonly: true', 'ingest:', ' adapters:', ' - live-database', diff --git a/packages/context/src/scan/relationship-artifacts.test.ts b/packages/context/src/scan/relationship-artifacts.test.ts index e5fd0190..c366f1b2 100644 --- a/packages/context/src/scan/relationship-artifacts.test.ts +++ b/packages/context/src/scan/relationship-artifacts.test.ts @@ -24,7 +24,6 @@ async function writeWarehouseConfig(projectDir: string): Promise { ' warehouse:', ' driver: sqlite', ' path: warehouse.db', - ' readonly: true', 'ingest:', ' adapters:', ' - live-database', diff --git a/packages/context/src/scan/relationship-review-decisions.test.ts b/packages/context/src/scan/relationship-review-decisions.test.ts index 3b92e345..30277c57 100644 --- a/packages/context/src/scan/relationship-review-decisions.test.ts +++ b/packages/context/src/scan/relationship-review-decisions.test.ts @@ -28,7 +28,6 @@ async function createProject(projectDir: string): Promise { ' warehouse:', ' driver: sqlite', ' path: warehouse.db', - ' readonly: true', 'ingest:', ' adapters:', ' - live-database', diff --git a/packages/context/src/sl/local-query.test.ts b/packages/context/src/sl/local-query.test.ts index 80209329..4105c9f6 100644 --- a/packages/context/src/sl/local-query.test.ts +++ b/packages/context/src/sl/local-query.test.ts @@ -14,7 +14,7 @@ describe('compileLocalSlQuery', () => { beforeEach(async () => { tempDir = await mkdtemp(join(tmpdir(), 'ktx-local-query-')); project = await initKtxProject({ projectDir: join(tempDir, 'project'), projectName: 'warehouse' }); - project.config.connections.warehouse = { driver: 'postgres', readonly: true }; + project.config.connections.warehouse = { driver: 'postgres' }; await project.fileStore.writeFile( 'semantic-layer/warehouse/orders.yaml', `name: orders @@ -222,7 +222,7 @@ grain: [] expect(queryExecutor.execute).toHaveBeenCalledWith({ connectionId: 'warehouse', projectDir: project.projectDir, - connection: { driver: 'postgres', readonly: true }, + connection: { driver: 'postgres' }, sql: 'select status, count(*) as order_count from public.orders group by status', maxRows: 10, }); @@ -248,7 +248,7 @@ grain: [] }); it('requires connectionId when multiple connections are configured', async () => { - project.config.connections.analytics = { driver: 'bigquery', readonly: true }; + project.config.connections.analytics = { driver: 'bigquery' }; await expect( compileLocalSlQuery(project, { diff --git a/scripts/build-adventureworks-oltp-fixture.mjs b/scripts/build-adventureworks-oltp-fixture.mjs index 4ac2e5ba..2a57fe85 100644 --- a/scripts/build-adventureworks-oltp-fixture.mjs +++ b/scripts/build-adventureworks-oltp-fixture.mjs @@ -231,7 +231,6 @@ async function main() { driver: 'sqlserver', url, schemas: ['dbo', 'HumanResources', 'Person', 'Production', 'Purchasing', 'Sales'], - readonly: true, trustServerCertificate: true, }, now: () => new Date('2026-05-07T00:00:00.000Z'), diff --git a/scripts/examples-docs.test.mjs b/scripts/examples-docs.test.mjs index 79e26f74..47fe3f10 100644 --- a/scripts/examples-docs.test.mjs +++ b/scripts/examples-docs.test.mjs @@ -50,7 +50,6 @@ describe('standalone example docs', () => { config, /path: \.\.\/\.\.\/packages\/context\/test\/fixtures\/relationship-benchmarks\/orbit_style_product_no_declared_constraints\/data\.sqlite/, ); - assert.match(config, /readonly: true/); assert.match(config, /llm_proposals: false/); assert.match(config, /validation_required_for_manifest: true/); }); diff --git a/scripts/installed-live-database-smoke.mjs b/scripts/installed-live-database-smoke.mjs index 653f53b9..88213366 100644 --- a/scripts/installed-live-database-smoke.mjs +++ b/scripts/installed-live-database-smoke.mjs @@ -92,7 +92,6 @@ export function buildKtxYaml(postgresUrl) { ' warehouse:', ' driver: postgres', ` url: "${postgresUrl}"`, - ' readonly: true', 'storage:', ' state: sqlite', ' search: sqlite-fts5', diff --git a/scripts/installed-live-database-smoke.test.mjs b/scripts/installed-live-database-smoke.test.mjs index c62e98b0..0a45cdf5 100644 --- a/scripts/installed-live-database-smoke.test.mjs +++ b/scripts/installed-live-database-smoke.test.mjs @@ -59,7 +59,6 @@ describe('installed live-database artifact smoke helpers', () => { ' warehouse:', ' driver: postgres', ' url: "postgresql://ktx:postgres@127.0.0.1:15432/warehouse"', // pragma: allowlist secret - ' readonly: true', 'storage:', ' state: sqlite', ' search: sqlite-fts5', diff --git a/scripts/package-artifacts.mjs b/scripts/package-artifacts.mjs index b74b4277..73f51ec4 100644 --- a/scripts/package-artifacts.mjs +++ b/scripts/package-artifacts.mjs @@ -646,7 +646,6 @@ try { ' warehouse:', ' driver: sqlite', ' path: warehouse.db', - ' readonly: true', 'storage:', ' state: sqlite', ' search: sqlite-fts5',