Skip to content
Merged

dev #183

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
8e4c3b9
fix(ui): prevent text overflow for long paths in DataLake components
Nuri1977 Jan 6, 2026
16d7136
refactor(datalake): implement "Refresh" functionality in DataLake and…
Nuri1977 Jan 6, 2026
da1c7d8
fix: eslint warning
Nuri1977 Jan 6, 2026
6a42cf5
feat(datalake): implement snapshot pagination, robust csv import, and…
Nuri1977 Jan 6, 2026
5ee4b04
fix(datalake): fix PostgreSQL connection string encoding
Nuri1977 Jan 6, 2026
1fdcb12
fix linting
Nuri1977 Jan 6, 2026
dbe72fe
fix: typescript error
Nuri1977 Jan 6, 2026
5ad12a2
Fix SQL injection vulnerability in DataLakeTableImportWizard by escap…
Nuri1977 Jan 6, 2026
5e1daf8
fix: update editor tabs on file rename and folder deletion
Nuri1977 Jan 9, 2026
0e10cc2
Fixes issue where:
Nuri1977 Jan 12, 2026
8a16b23
feat: implement SQL query cancellation and refine result UI
Nuri1977 Jan 12, 2026
2030d60
Change styles of Alerts and fix history comments
Nuri1977 Jan 12, 2026
a723caa
fix: update request type generic in connectors service
Nuri1977 Jan 12, 2026
6a0d09a
Fix: Remove depreciated config from profiles.yml
Nuri1977 Jan 12, 2026
afa5dc5
fix(editor): prevent tab content corruption when opening non-editable…
Nuri1977 Jan 12, 2026
dcdd83e
Merge pull request #179 from rosettadb/fix/monaco-editor-tabs-delete-…
jasir99 Jan 12, 2026
4ff7920
Merge pull request #180 from rosettadb/enhance/sql-eitor-stop-and-his…
jasir99 Jan 12, 2026
0cc6aae
refactor(dataLake): migrate TextField InputProps to MUI v6 slotProps
Nuri1977 Jan 12, 2026
eba92a1
feat(dataLake): implement instance snapshot history with pagination
Nuri1977 Jan 12, 2026
c1d26b5
fix(dataLake): handle zero/negative bytes in formatBytes to prevent NaN
Nuri1977 Jan 12, 2026
b9fb52f
fix(dataLake): address SQL injection, syntax errors, and edge cases
Nuri1977 Jan 12, 2026
eb25398
fix(datalake): escape SQL LIKE wildcards in snapshot filters
Nuri1977 Jan 12, 2026
81d9100
Merge pull request #181 from rosettadb/fix/profile-yml-file-changes
jasir99 Jan 12, 2026
71f315b
Merge pull request #174 from rosettadb/fix/ducklake-text-overlay-and-…
jasir99 Jan 12, 2026
ffa99ae
version bump: 1.2.6
jasir99 Jan 13, 2026
1c0279a
Merge pull request #182 from rosettadb/version/1.2.6
jasir99 Jan 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "1.2.5",
"version": "1.2.6",
"name": "rosetta-dbt-studio",
"description": "Turn Raw Data into Business Insights—Faster with RosettaDB",
"keywords": [
Expand Down
4 changes: 2 additions & 2 deletions release/app/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion release/app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "rosetta-dbt-studio",
"version": "1.2.5",
"version": "1.2.6",
"description": "A modern DBT desktop IDE",
"license": "MIT",
"author": {
Expand Down
19 changes: 14 additions & 5 deletions src/main/ipcHandlers/connectors.ipcHandlers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { ipcMain } from 'electron';
import { ConnectorsService } from '../services';
import type { ConnectionInput, QueryResponseType } from '../../types/backend';
import type {
ConnectionInput,
QueryResponseType,
ExecuteStatementType,
} from '../../types/backend';
import { ConfigureConnectionBody, UpdateConnectionBody } from '../../types/ipc';
import { CloudConnection, RecentItem } from '../../types/frontend';

Expand All @@ -10,6 +14,7 @@ const handlerChannels = [
'connector:validate',
'connector:getJdbcUrl',
'connector:query',
'connector:cancel-query',
'connector:list',
];

Expand Down Expand Up @@ -67,10 +72,7 @@ const registerConnectorsHandlers = () => {

ipcMain.handle(
'connector:query',
async (
_event,
body: { connection: ConnectionInput; query: string; projectName: string },
): Promise<QueryResponseType> => {
async (_event, body: ExecuteStatementType): Promise<QueryResponseType> => {
try {
return ConnectorsService.executeSelectStatement(body);
} catch (error: any) {
Expand All @@ -79,6 +81,13 @@ const registerConnectorsHandlers = () => {
},
);

ipcMain.handle(
'connector:cancel-query',
async (_event, queryId: string): Promise<void> => {
return ConnectorsService.cancelQuery(queryId);
},
);

ipcMain.handle(
'connector:setConnectionEnvVariable',
async (_event, { key, value }: { key: string; value: string }) => {
Expand Down
9 changes: 9 additions & 0 deletions src/main/ipcHandlers/duckLake.ipcHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,15 @@ const registerDuckLakeHandlers = () => {
},
);

ipcMain.handle(
'ducklake:instance:listSnapshots',
async (_event, instanceId: string, params: any) => {
// Ensure params has defaults if missing (though Service also defaults)
const listParams = params || { page: 1, pageSize: 100 };
return DuckLakeService.listInstanceSnapshots(instanceId, listParams);
},
);

ipcMain.handle(
'ducklake:snapshot:restore',
async (
Expand Down
98 changes: 76 additions & 22 deletions src/main/services/connectors.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -347,13 +347,25 @@ export default class ConnectorsService {
}
}

private static runningQueries = new Map<string, () => void>();

static async cancelQuery(queryId: string): Promise<void> {
const cancelFn = this.runningQueries.get(queryId);
if (cancelFn) {
// Execute the cancellation function (closes connection/client)
cancelFn();
this.runningQueries.delete(queryId);
}
}

/**
* Run a select statement and expect the results and fields
*/
static async executeSelectStatement({
connection,
query,
projectName,
queryId,
}: ExecuteStatementType): Promise<QueryResponseType> {
const storeUser = await SecureStorageService.getCredential(
`db-user-${projectName}`,
Expand Down Expand Up @@ -384,25 +396,71 @@ export default class ConnectorsService {
(connection as any).keyfile = bigQueryKey;
}

switch (connection.type) {
case 'postgres':
return executePostgresQuery(connection, query);
case 'snowflake':
return executeSnowflakeQuery(connection, query);
case 'bigquery':
return executeBigQueryQuery(connection, query);
case 'databricks':
return executeDatabricksQuery(connection, query);
case 'duckdb':
return executeDuckDBQuery(connection, query);
case 'redshift':
return executeRedshiftQuery(connection, query);
default:
// Use the literal type instead of accessing the property to avoid TypeScript error
throw new Error(
`Unsupported connection type: ${(connection as any).type}`,
);
const startTime = Date.now();
let response: QueryResponseType;

// Helper to register cancel callback if queryId is present
const registerCancel = queryId
? (fn: () => void) => {
this.runningQueries.set(queryId, fn);
}
: undefined;

try {
switch (connection.type) {
case 'postgres':
response = await executePostgresQuery(
connection,
query,
registerCancel,
);
break;
case 'snowflake':
response = await executeSnowflakeQuery(
connection,
query,
registerCancel,
);
break;
case 'bigquery':
// BigQuery cancellation not yet implemented in utils
response = await executeBigQueryQuery(connection, query);
break;
case 'databricks':
response = await executeDatabricksQuery(
connection,
query,
registerCancel,
);
break;
case 'duckdb':
response = await executeDuckDBQuery(
connection,
query,
registerCancel,
);
break;
case 'redshift':
response = await executeRedshiftQuery(
connection,
query,
registerCancel,
);
break;
default:
throw new Error(
`Unsupported connection type: ${(connection as any).type}`,
);
}
} finally {
// Clean up running query registry
if (queryId) {
this.runningQueries.delete(queryId);
}
}

response.duration = Date.now() - startTime;
return response;
}

static extractDbNameFromPath = (url: string) => {
Expand Down Expand Up @@ -637,10 +695,6 @@ export default class ConnectorsService {
conn: ConnectionInput,
): Promise<string> {
const profileConfig = {
config: {
send_anonymous_usage_stats: false,
partial_parse: true,
},
[name]: {
target: 'dev',
outputs: {
Expand Down
21 changes: 21 additions & 0 deletions src/main/services/duckLake.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,15 @@ import {
DuckLakeInstanceHealth,
DuckLakeTableInfo,
DuckLakeSnapshotInfo,
DuckLakeSnapshotDetail,
DuckLakeQueryRequest,
DuckLakeQueryResult,
DuckLakeMaintenanceTask,
DuckLakeCatalogConfig,
DuckLakeMaintenanceType,
DuckLakeStorageConfig,
DuckLakeSnapshotParams,
DuckLakePaginatedResult,
} from '../../types/duckLake';
import { DuckLakeError } from '../../types/duckLakeErrors';

Expand Down Expand Up @@ -563,6 +566,24 @@ export default class DuckLakeService {
}
}

static async listInstanceSnapshots(
instanceId: string,
params: DuckLakeSnapshotParams = { page: 1, pageSize: 100 },
): Promise<DuckLakePaginatedResult<DuckLakeSnapshotDetail>> {
try {
await this.ensureConnected(instanceId);
const adapter = await this.getAdapter(instanceId);
return await adapter.listInstanceSnapshots(params);
} catch (error) {
// eslint-disable-next-line no-console
console.error(
`[DuckLakeService] Failed to list instance snapshots for ${instanceId}:`,
error,
);
throw error;
}
}

static async restoreSnapshot(
instanceId: string,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
Expand Down
11 changes: 11 additions & 0 deletions src/main/services/duckLake/adapters/base.adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@ import {
DuckLakeInstance,
DuckLakeTableInfo,
DuckLakeSnapshotInfo,
DuckLakeSnapshotDetail,
DuckLakeQueryResult,
DuckLakeQueryRequest,
DuckLakeStorageConfig,
DuckLakeSnapshotParams,
DuckLakePaginatedResult,
} from '../../../../types/duckLake';
import { generateGCSBearerToken } from '../../../helpers/cloudAuth.helper';

Expand Down Expand Up @@ -96,6 +99,14 @@ export abstract class CatalogAdapter {
*/
abstract listSnapshots(tableName: string): Promise<DuckLakeSnapshotInfo[]>;

/**
* Get all snapshots for the entire instance (not table-specific)
* Used for instance-wide history view with pagination
*/
abstract listInstanceSnapshots(
params: DuckLakeSnapshotParams,
): Promise<DuckLakePaginatedResult<DuckLakeSnapshotDetail>>;

/**
* Get comprehensive table details from DuckLake metadata catalog (Phase 8b)
* Queries multiple metadata tables to provide complete table information
Expand Down
Loading