@sqlrooms/sql-editor 0.26.1-rc.4 → 0.26.1-rc.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"SqlEditor.d.ts","sourceRoot":"","sources":["../src/SqlEditor.tsx"],"names":[],"mappings":"AAQA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAGnD,OAAO,EAAC,gBAAgB,EAAC,MAAM,+BAA+B,CAAC;AAE/D,OAAO,EAEL,wBAAwB,EACzB,MAAM,kCAAkC,CAAC;AAE1C,MAAM,MAAM,cAAc,GAAG;IAC3B;;0EAEsE;IACtE,MAAM,CAAC,EAAE,wBAAwB,CAAC,QAAQ,CAAC,CAAC;IAC5C,kDAAkD;IAClD,MAAM,EAAE,OAAO,CAAC;IAChB,uEAAuE;IACvE,kBAAkB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACrC;;;OAGG;IACH,gBAAgB,CAAC,EAAE,IAAI,CACrB,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,EAC7C,YAAY,GAAG,kBAAkB,CAClC,CAAC;IACF,0DAA0D;IAC1D,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAEF,QAAA,MAAM,SAAS,4CAwFb,CAAC;AAEH,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"SqlEditor.d.ts","sourceRoot":"","sources":["../src/SqlEditor.tsx"],"names":[],"mappings":"AAOA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAGnD,OAAO,EAAC,gBAAgB,EAAC,MAAM,+BAA+B,CAAC;AAE/D,OAAO,EAEL,wBAAwB,EACzB,MAAM,kCAAkC,CAAC;AAG1C,MAAM,MAAM,cAAc,GAAG;IAC3B;;0EAEsE;IACtE,MAAM,CAAC,EAAE,wBAAwB,CAAC,QAAQ,CAAC,CAAC;IAC5C,kDAAkD;IAClD,MAAM,EAAE,OAAO,CAAC;IAChB,uEAAuE;IACvE,kBAAkB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACrC;;;OAGG;IACH,gBAAgB,CAAC,EAAE,IAAI,CACrB,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,EAC7C,YAAY,GAAG,kBAAkB,CAClC,CAAC;IACF,0DAA0D;IAC1D,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAEF,QAAA,MAAM,SAAS,4CA8Eb,CAAC;AAEH,eAAe,SAAS,CAAC"}
package/dist/SqlEditor.js CHANGED
@@ -1,5 +1,4 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useBaseRoomShellStore } from '@sqlrooms/room-shell';
3
2
  import { Button, ResizableHandle, ResizablePanel, ResizablePanelGroup, } from '@sqlrooms/ui';
4
3
  import { PlusIcon } from 'lucide-react';
5
4
  import React, { useCallback, useState } from 'react';
@@ -11,15 +10,7 @@ import { TableStructurePanel, } from './components/TableStructurePanel';
11
10
  import { useStoreWithSqlEditor } from './SqlEditorSlice';
12
11
  const SqlEditor = React.memo((props) => {
13
12
  const { schema = '*', documentationPanel, queryResultProps } = props;
14
- // Store access
15
- const addOrUpdateSqlQueryDataSource = useBaseRoomShellStore((state) => state.room.addOrUpdateSqlQueryDataSource);
16
- const lastQueryStatement = useStoreWithSqlEditor((s) => s.sqlEditor.queryResult?.status === 'success' &&
17
- s.sqlEditor.queryResult?.type === 'select'
18
- ? s.sqlEditor.queryResult.lastQueryStatement
19
- : '');
20
- // const currentQuery = useStoreWithSqlEditor((s) =>
21
- // s.sqlEditor.getCurrentQuery(),
22
- // );
13
+ const lastQuery = useStoreWithSqlEditor(({ sqlEditor: { queryResult: qr } }) => qr?.status === 'success' && qr?.type === 'select' ? qr.query : '');
23
14
  // UI state
24
15
  const [showDocs, setShowDocs] = useState(false);
25
16
  const [createTableModalOpen, setCreateTableModalOpen] = useState(false);
@@ -30,7 +21,7 @@ const SqlEditor = React.memo((props) => {
30
21
  const handleCreateTable = useCallback(() => {
31
22
  setCreateTableModalOpen(true);
32
23
  }, []);
33
- return (_jsxs("div", { className: "relative flex h-full w-full flex-col overflow-hidden", children: [_jsx(SqlEditorHeader, { title: "SQL Editor", showDocs: showDocs, documentationPanel: documentationPanel, onToggleDocs: handleToggleDocs }), _jsx("div", { className: "bg-muted h-full flex-grow", children: _jsxs(ResizablePanelGroup, { direction: "horizontal", className: "h-full", children: [_jsx(ResizablePanel, { defaultSize: showDocs ? 70 : 100, children: _jsxs(ResizablePanelGroup, { direction: "vertical", className: "h-full", children: [_jsx(ResizablePanel, { defaultSize: 50, className: "flex flex-row", children: _jsxs(ResizablePanelGroup, { direction: "horizontal", children: [_jsx(ResizablePanel, { defaultSize: 20, children: _jsx(TableStructurePanel, { schema: schema }) }), _jsx(ResizableHandle, { withHandle: true }), _jsx(ResizablePanel, { defaultSize: 80, children: _jsx(QueryEditorPanel, {}) })] }) }), _jsx(ResizableHandle, { withHandle: true }), _jsx(ResizablePanel, { defaultSize: 50, children: _jsx(QueryResultPanel, { onRowClick: queryResultProps?.onRowClick, onRowDoubleClick: queryResultProps?.onRowDoubleClick, renderActions: () => (_jsx("div", { className: "flex gap-2", children: _jsxs(Button, { size: "xs", onClick: handleCreateTable, children: [_jsx(PlusIcon, { className: "h-4 w-4" }), "New table"] }) })) }) })] }) }), showDocs && (_jsxs(_Fragment, { children: [_jsx(ResizableHandle, { withHandle: true }), _jsx(ResizablePanel, { defaultSize: 30, children: documentationPanel })] }))] }) }), _jsx(CreateTableModal, { query: lastQueryStatement, isOpen: createTableModalOpen, onClose: () => setCreateTableModalOpen(false), onAddOrUpdateSqlQuery: addOrUpdateSqlQueryDataSource })] }));
24
+ return (_jsxs("div", { className: "relative flex h-full w-full flex-col overflow-hidden", children: [_jsx(SqlEditorHeader, { title: "SQL Editor", showDocs: showDocs, documentationPanel: documentationPanel, onToggleDocs: handleToggleDocs }), _jsx("div", { className: "bg-muted h-full flex-grow", children: _jsxs(ResizablePanelGroup, { direction: "horizontal", className: "h-full", children: [_jsx(ResizablePanel, { defaultSize: showDocs ? 70 : 100, children: _jsxs(ResizablePanelGroup, { direction: "vertical", className: "h-full", children: [_jsx(ResizablePanel, { defaultSize: 50, className: "flex flex-row", children: _jsxs(ResizablePanelGroup, { direction: "horizontal", children: [_jsx(ResizablePanel, { defaultSize: 20, children: _jsx(TableStructurePanel, { schema: schema }) }), _jsx(ResizableHandle, { withHandle: true }), _jsx(ResizablePanel, { defaultSize: 80, children: _jsx(QueryEditorPanel, {}) })] }) }), _jsx(ResizableHandle, { withHandle: true }), _jsx(ResizablePanel, { defaultSize: 50, children: _jsx(QueryResultPanel, { onRowClick: queryResultProps?.onRowClick, onRowDoubleClick: queryResultProps?.onRowDoubleClick, renderActions: () => (_jsx("div", { className: "flex gap-2", children: _jsxs(Button, { size: "xs", onClick: handleCreateTable, children: [_jsx(PlusIcon, { className: "h-4 w-4" }), "New table"] }) })) }) })] }) }), showDocs && (_jsxs(_Fragment, { children: [_jsx(ResizableHandle, { withHandle: true }), _jsx(ResizablePanel, { defaultSize: 30, children: documentationPanel })] }))] }) }), _jsx(CreateTableModal, { query: lastQuery, isOpen: createTableModalOpen, onClose: () => setCreateTableModalOpen(false), allowMultipleStatements: true, showSchemaSelection: true })] }));
34
25
  });
35
26
  export default SqlEditor;
36
27
  //# sourceMappingURL=SqlEditor.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"SqlEditor.js","sourceRoot":"","sources":["../src/SqlEditor.tsx"],"names":[],"mappings":"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","sourcesContent":["import {useBaseRoomShellStore} from '@sqlrooms/room-shell';\nimport {\n Button,\n ResizableHandle,\n ResizablePanel,\n ResizablePanelGroup,\n} from '@sqlrooms/ui';\nimport {PlusIcon} from 'lucide-react';\nimport React, {useCallback, useState} from 'react';\nimport CreateTableModal from './components/CreateTableModal';\nimport {QueryEditorPanel} from './components/QueryEditorPanel';\nimport {QueryResultPanel} from './components/QueryResultPanel';\nimport {SqlEditorHeader} from './components/SqlEditorHeader';\nimport {\n TableStructurePanel,\n TableStructurePanelProps,\n} from './components/TableStructurePanel';\nimport {useStoreWithSqlEditor} from './SqlEditorSlice';\nexport type SqlEditorProps = {\n /** The database schema to use. Defaults to '*'.\n * If '*' is provided, all tables will be shown.\n * If a function is provided, it will be used to filter the tables. */\n schema?: TableStructurePanelProps['schema'];\n /** Whether the SQL editor is currently visible */\n isOpen: boolean;\n /** Optional component to render SQL documentation in the side panel */\n documentationPanel?: React.ReactNode;\n /**\n * Props forwarded to `QueryResultPanel` to configure result behavior.\n * This provides a single entry point for table interactions.\n */\n queryResultProps?: Pick<\n React.ComponentProps<typeof QueryResultPanel>,\n 'onRowClick' | 'onRowDoubleClick'\n >;\n /** Callback fired when the SQL editor should be closed */\n onClose: () => void;\n};\n\nconst SqlEditor = React.memo<SqlEditorProps>((props) => {\n const {schema = '*', documentationPanel, queryResultProps} = props;\n\n // Store access\n const addOrUpdateSqlQueryDataSource = useBaseRoomShellStore(\n (state) => state.room.addOrUpdateSqlQueryDataSource,\n );\n const lastQueryStatement = useStoreWithSqlEditor((s) =>\n s.sqlEditor.queryResult?.status === 'success' &&\n s.sqlEditor.queryResult?.type === 'select'\n ? s.sqlEditor.queryResult.lastQueryStatement\n : '',\n );\n // const currentQuery = useStoreWithSqlEditor((s) =>\n // s.sqlEditor.getCurrentQuery(),\n // );\n\n // UI state\n const [showDocs, setShowDocs] = useState(false);\n const [createTableModalOpen, setCreateTableModalOpen] = useState(false);\n\n // Handlers\n const handleToggleDocs = useCallback((show: boolean) => {\n setShowDocs(show);\n }, []);\n\n const handleCreateTable = useCallback(() => {\n setCreateTableModalOpen(true);\n }, []);\n\n return (\n <div className=\"relative flex h-full w-full flex-col overflow-hidden\">\n <SqlEditorHeader\n title=\"SQL Editor\"\n showDocs={showDocs}\n documentationPanel={documentationPanel}\n onToggleDocs={handleToggleDocs}\n />\n <div className=\"bg-muted h-full flex-grow\">\n <ResizablePanelGroup direction=\"horizontal\" className=\"h-full\">\n <ResizablePanel defaultSize={showDocs ? 70 : 100}>\n <ResizablePanelGroup direction=\"vertical\" className=\"h-full\">\n <ResizablePanel defaultSize={50} className=\"flex flex-row\">\n <ResizablePanelGroup direction=\"horizontal\">\n <ResizablePanel defaultSize={20}>\n <TableStructurePanel schema={schema} />\n </ResizablePanel>\n <ResizableHandle withHandle />\n <ResizablePanel defaultSize={80}>\n <QueryEditorPanel />\n </ResizablePanel>\n </ResizablePanelGroup>\n </ResizablePanel>\n <ResizableHandle withHandle />\n <ResizablePanel defaultSize={50}>\n <QueryResultPanel\n onRowClick={queryResultProps?.onRowClick}\n onRowDoubleClick={queryResultProps?.onRowDoubleClick}\n renderActions={() => (\n <div className=\"flex gap-2\">\n <Button size=\"xs\" onClick={handleCreateTable}>\n <PlusIcon className=\"h-4 w-4\" />\n New table\n </Button>\n </div>\n )}\n />\n </ResizablePanel>\n </ResizablePanelGroup>\n </ResizablePanel>\n {showDocs && (\n <>\n <ResizableHandle withHandle />\n <ResizablePanel defaultSize={30}>\n {documentationPanel}\n </ResizablePanel>\n </>\n )}\n </ResizablePanelGroup>\n </div>\n <CreateTableModal\n query={lastQueryStatement}\n isOpen={createTableModalOpen}\n onClose={() => setCreateTableModalOpen(false)}\n onAddOrUpdateSqlQuery={addOrUpdateSqlQueryDataSource}\n />\n </div>\n );\n});\n\nexport default SqlEditor;\n"]}
1
+ {"version":3,"file":"SqlEditor.js","sourceRoot":"","sources":["../src/SqlEditor.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n Button,\n ResizableHandle,\n ResizablePanel,\n ResizablePanelGroup,\n} from '@sqlrooms/ui';\nimport {PlusIcon} from 'lucide-react';\nimport React, {useCallback, useState} from 'react';\nimport CreateTableModal from './components/CreateTableModal';\nimport {QueryEditorPanel} from './components/QueryEditorPanel';\nimport {QueryResultPanel} from './components/QueryResultPanel';\nimport {SqlEditorHeader} from './components/SqlEditorHeader';\nimport {\n TableStructurePanel,\n TableStructurePanelProps,\n} from './components/TableStructurePanel';\nimport {useStoreWithSqlEditor} from './SqlEditorSlice';\n\nexport type SqlEditorProps = {\n /** The database schema to use. Defaults to '*'.\n * If '*' is provided, all tables will be shown.\n * If a function is provided, it will be used to filter the tables. */\n schema?: TableStructurePanelProps['schema'];\n /** Whether the SQL editor is currently visible */\n isOpen: boolean;\n /** Optional component to render SQL documentation in the side panel */\n documentationPanel?: React.ReactNode;\n /**\n * Props forwarded to `QueryResultPanel` to configure result behavior.\n * This provides a single entry point for table interactions.\n */\n queryResultProps?: Pick<\n React.ComponentProps<typeof QueryResultPanel>,\n 'onRowClick' | 'onRowDoubleClick'\n >;\n /** Callback fired when the SQL editor should be closed */\n onClose: () => void;\n};\n\nconst SqlEditor = React.memo<SqlEditorProps>((props) => {\n const {schema = '*', documentationPanel, queryResultProps} = props;\n\n const lastQuery = useStoreWithSqlEditor(({sqlEditor: {queryResult: qr}}) =>\n qr?.status === 'success' && qr?.type === 'select' ? qr.query : '',\n );\n // UI state\n const [showDocs, setShowDocs] = useState(false);\n const [createTableModalOpen, setCreateTableModalOpen] = useState(false);\n\n // Handlers\n const handleToggleDocs = useCallback((show: boolean) => {\n setShowDocs(show);\n }, []);\n\n const handleCreateTable = useCallback(() => {\n setCreateTableModalOpen(true);\n }, []);\n\n return (\n <div className=\"relative flex h-full w-full flex-col overflow-hidden\">\n <SqlEditorHeader\n title=\"SQL Editor\"\n showDocs={showDocs}\n documentationPanel={documentationPanel}\n onToggleDocs={handleToggleDocs}\n />\n <div className=\"bg-muted h-full flex-grow\">\n <ResizablePanelGroup direction=\"horizontal\" className=\"h-full\">\n <ResizablePanel defaultSize={showDocs ? 70 : 100}>\n <ResizablePanelGroup direction=\"vertical\" className=\"h-full\">\n <ResizablePanel defaultSize={50} className=\"flex flex-row\">\n <ResizablePanelGroup direction=\"horizontal\">\n <ResizablePanel defaultSize={20}>\n <TableStructurePanel schema={schema} />\n </ResizablePanel>\n <ResizableHandle withHandle />\n <ResizablePanel defaultSize={80}>\n <QueryEditorPanel />\n </ResizablePanel>\n </ResizablePanelGroup>\n </ResizablePanel>\n <ResizableHandle withHandle />\n <ResizablePanel defaultSize={50}>\n <QueryResultPanel\n onRowClick={queryResultProps?.onRowClick}\n onRowDoubleClick={queryResultProps?.onRowDoubleClick}\n renderActions={() => (\n <div className=\"flex gap-2\">\n <Button size=\"xs\" onClick={handleCreateTable}>\n <PlusIcon className=\"h-4 w-4\" />\n New table\n </Button>\n </div>\n )}\n />\n </ResizablePanel>\n </ResizablePanelGroup>\n </ResizablePanel>\n {showDocs && (\n <>\n <ResizableHandle withHandle />\n <ResizablePanel defaultSize={30}>\n {documentationPanel}\n </ResizablePanel>\n </>\n )}\n </ResizablePanelGroup>\n </div>\n <CreateTableModal\n query={lastQuery}\n isOpen={createTableModalOpen}\n onClose={() => setCreateTableModalOpen(false)}\n allowMultipleStatements={true}\n showSchemaSelection={true}\n />\n </div>\n );\n});\n\nexport default SqlEditor;\n"]}
@@ -14,10 +14,12 @@ export type QueryResult = {
14
14
  status: 'success';
15
15
  type: 'pragma' | 'explain' | 'select';
16
16
  result: arrow.Table | undefined;
17
+ query: string;
17
18
  lastQueryStatement: string;
18
19
  } | {
19
20
  status: 'success';
20
21
  type: 'exec';
22
+ query: string;
21
23
  lastQueryStatement: string;
22
24
  };
23
25
  export declare function isQueryWithResult(queryResult: QueryResult | undefined): queryResult is QueryResult & {
@@ -85,9 +87,14 @@ export type SqlEditorSliceState = {
85
87
  closeQueryTab(queryId: string): void;
86
88
  /**
87
89
  * Open a closed tab id.
88
- * @param queryId - The ID of the query to remove.
90
+ * @param queryId - The ID of the query to open.
89
91
  */
90
92
  openQueryTab(queryId: string): void;
93
+ /**
94
+ * Set the list of open tab IDs. Used for reordering or opening tabs.
95
+ * @param tabIds - The new list of open tab IDs.
96
+ */
97
+ setOpenTabs(tabIds: string[]): void;
91
98
  /**
92
99
  * Update the SQL text for a query.
93
100
  * @param queryId - The ID of the query to update.
@@ -1 +1 @@
1
- {"version":3,"file":"SqlEditorSlice.d.ts","sourceRoot":"","sources":["../src/SqlEditorSlice.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"SqlEditorSlice.d.ts","sourceRoot":"","sources":["../src/SqlEditorSlice.tsx"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { createId } from '@paralleldrive/cuid2';
2
- import { getSqlErrorWithPointer, makeLimitQuery, splitSqlStatements, } from '@sqlrooms/duckdb';
2
+ import { getSqlErrorWithPointer, joinStatements, makeLimitQuery, separateLastStatement, } from '@sqlrooms/duckdb';
3
3
  import { createSlice, useBaseRoomShellStore, } from '@sqlrooms/room-shell';
4
4
  import { createDefaultSqlEditorConfig, } from '@sqlrooms/sql-editor-config';
5
5
  import { generateUniqueName } from '@sqlrooms/utils';
@@ -43,6 +43,7 @@ export function createSqlEditorSlice({ config = createDefaultSqlEditorConfig(),
43
43
  };
44
44
  set((state) => produce(state, (draft) => {
45
45
  draft.sqlEditor.config.queries.push(newQuery);
46
+ draft.sqlEditor.config.openTabs.push(newQuery.id);
46
47
  draft.sqlEditor.config.selectedQueryId = newQuery.id;
47
48
  }));
48
49
  return newQuery;
@@ -50,25 +51,38 @@ export function createSqlEditorSlice({ config = createDefaultSqlEditorConfig(),
50
51
  deleteQueryTab: (queryId) => {
51
52
  const sqlEditorConfig = get().sqlEditor.config;
52
53
  const queries = sqlEditorConfig.queries;
54
+ const openTabs = sqlEditorConfig.openTabs;
53
55
  if (queries.length <= 1) {
54
56
  // Don't delete the last query
55
57
  return;
56
58
  }
57
- const index = queries.findIndex((q) => q.id === queryId);
58
- if (index === -1)
59
- return;
60
- const isSelected = sqlEditorConfig.selectedQueryId === queryId;
59
+ const wasSelected = sqlEditorConfig.selectedQueryId === queryId;
60
+ const deletingOpenIndex = openTabs.indexOf(queryId);
61
61
  const filteredQueries = queries.filter((q) => q.id !== queryId);
62
62
  set((state) => produce(state, (draft) => {
63
63
  draft.sqlEditor.config.queries = filteredQueries;
64
- // If we're deleting the selected tab, select the previous one or the first one
65
- if (isSelected && filteredQueries.length > 0) {
66
- const newSelectedIndex = Math.max(0, index - 1);
67
- // Safely access the ID with fallback to the first query if needed
68
- const newSelectedId = filteredQueries[newSelectedIndex]?.id ??
69
- filteredQueries[0]?.id;
70
- if (newSelectedId) {
71
- draft.sqlEditor.config.selectedQueryId = newSelectedId;
64
+ draft.sqlEditor.config.openTabs = openTabs.filter((id) => id !== queryId);
65
+ // If we deleted the selected query, select another one
66
+ if (wasSelected) {
67
+ const newOpenTabs = draft.sqlEditor.config.openTabs;
68
+ const remainingQueries = draft.sqlEditor.config.queries;
69
+ if (newOpenTabs.length > 0) {
70
+ // Select from remaining open tabs
71
+ const newIndex = deletingOpenIndex === 0
72
+ ? 0
73
+ : Math.min(deletingOpenIndex - 1, newOpenTabs.length - 1);
74
+ const newSelectedId = newOpenTabs[newIndex];
75
+ if (newSelectedId) {
76
+ draft.sqlEditor.config.selectedQueryId = newSelectedId;
77
+ }
78
+ }
79
+ else if (remainingQueries.length > 0) {
80
+ // No open tabs left, open a closed query
81
+ const queryToOpen = remainingQueries[0];
82
+ if (queryToOpen) {
83
+ draft.sqlEditor.config.openTabs.push(queryToOpen.id);
84
+ draft.sqlEditor.config.selectedQueryId = queryToOpen.id;
85
+ }
72
86
  }
73
87
  }
74
88
  }));
@@ -83,22 +97,23 @@ export function createSqlEditorSlice({ config = createDefaultSqlEditorConfig(),
83
97
  },
84
98
  closeQueryTab: (queryId) => {
85
99
  set((state) => produce(state, (draft) => {
86
- draft.sqlEditor.config.closedTabIds.push(queryId);
87
- const openedTabs = draft.sqlEditor.config.queries.filter((q) => !draft.sqlEditor.config.closedTabIds.includes(q.id));
88
- if (draft.sqlEditor.config.selectedQueryId === queryId &&
89
- openedTabs.length > 0 &&
90
- openedTabs[0]) {
91
- draft.sqlEditor.config.selectedQueryId = openedTabs[0].id;
92
- }
100
+ draft.sqlEditor.config.openTabs =
101
+ draft.sqlEditor.config.openTabs.filter((id) => id !== queryId);
93
102
  }));
94
103
  },
95
104
  openQueryTab: (queryId) => {
96
105
  set((state) => produce(state, (draft) => {
97
- draft.sqlEditor.config.closedTabIds =
98
- draft.sqlEditor.config.closedTabIds?.filter((id) => id !== queryId);
106
+ if (!draft.sqlEditor.config.openTabs.includes(queryId)) {
107
+ draft.sqlEditor.config.openTabs.push(queryId);
108
+ }
99
109
  draft.sqlEditor.config.selectedQueryId = queryId;
100
110
  }));
101
111
  },
112
+ setOpenTabs: (tabIds) => {
113
+ set((state) => produce(state, (draft) => {
114
+ draft.sqlEditor.config.openTabs = tabIds;
115
+ }));
116
+ },
102
117
  updateQueryText: (queryId, queryText) => {
103
118
  set((state) => produce(state, (draft) => {
104
119
  const query = draft.sqlEditor.config.queries.find((q) => q.id === queryId);
@@ -174,12 +189,8 @@ export function createSqlEditorSlice({ config = createDefaultSqlEditorConfig(),
174
189
  try {
175
190
  const connector = await get().db.getConnector();
176
191
  const signal = queryController.signal;
177
- const statements = splitSqlStatements(query);
178
- const allButLastStatements = statements.slice(0, -1);
179
- const lastQueryStatement = statements[statements.length - 1];
180
- if (!statements?.length) {
181
- throw new Error('Empty query');
182
- }
192
+ const { precedingStatements, lastStatement: lastQueryStatement } = separateLastStatement(query);
193
+ const hasMultipleStatements = precedingStatements.length > 0;
183
194
  if (signal.aborted) {
184
195
  throw new Error('Query aborted');
185
196
  }
@@ -190,17 +201,16 @@ export function createSqlEditorSlice({ config = createDefaultSqlEditorConfig(),
190
201
  const isValidSelectQuery = !parsedLastStatement.error;
191
202
  if (isValidSelectQuery) {
192
203
  // Add limit to the last statement
193
- const queryWithLimit = [
194
- ...allButLastStatements,
195
- makeLimitQuery(lastQueryStatement, {
196
- sanitize: false, // should already be sanitized
197
- limit: get().sqlEditor.queryResultLimit,
198
- }),
199
- ].join(';\n');
204
+ const limitedLastStatement = makeLimitQuery(lastQueryStatement, {
205
+ sanitize: false, // should already be sanitized
206
+ limit: get().sqlEditor.queryResultLimit,
207
+ });
208
+ const queryWithLimit = joinStatements(precedingStatements, limitedLastStatement);
200
209
  const result = await connector.query(queryWithLimit, { signal });
201
210
  queryResult = {
202
211
  status: 'success',
203
212
  type: 'select',
213
+ query,
204
214
  lastQueryStatement,
205
215
  result,
206
216
  };
@@ -220,6 +230,7 @@ export function createSqlEditorSlice({ config = createDefaultSqlEditorConfig(),
220
230
  queryResult = {
221
231
  status: 'success',
222
232
  type: 'explain',
233
+ query,
223
234
  lastQueryStatement,
224
235
  result,
225
236
  };
@@ -228,6 +239,7 @@ export function createSqlEditorSlice({ config = createDefaultSqlEditorConfig(),
228
239
  queryResult = {
229
240
  status: 'success',
230
241
  type: 'pragma',
242
+ query,
231
243
  lastQueryStatement,
232
244
  result,
233
245
  };
@@ -236,6 +248,7 @@ export function createSqlEditorSlice({ config = createDefaultSqlEditorConfig(),
236
248
  queryResult = {
237
249
  status: 'success',
238
250
  type: 'exec',
251
+ query,
239
252
  lastQueryStatement,
240
253
  };
241
254
  }
@@ -245,7 +258,7 @@ export function createSqlEditorSlice({ config = createDefaultSqlEditorConfig(),
245
258
  }
246
259
  // Refresh table schemas if there are multiple statements or if the
247
260
  // last statement is not a select query
248
- if (statements.length > 1 || !isValidSelectQuery) {
261
+ if (hasMultipleStatements || !isValidSelectQuery) {
249
262
  get().db.refreshTableSchemas();
250
263
  }
251
264
  if (signal.aborted) {
@@ -1 +1 @@
1
- {"version":3,"file":"SqlEditorSlice.js","sourceRoot":"","sources":["../src/SqlEditorSlice.tsx"],"names":[],"mappings":"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","sourcesContent":["import {createId} from '@paralleldrive/cuid2';\nimport {\n DuckDbSliceState,\n getSqlErrorWithPointer,\n makeLimitQuery,\n splitSqlStatements,\n} from '@sqlrooms/duckdb';\nimport {\n BaseRoomStoreState,\n createSlice,\n RoomShellSliceState,\n StateCreator,\n useBaseRoomShellStore,\n} from '@sqlrooms/room-shell';\nimport {\n createDefaultSqlEditorConfig,\n SqlEditorSliceConfig,\n} from '@sqlrooms/sql-editor-config';\nimport {generateUniqueName} from '@sqlrooms/utils';\nimport * as arrow from 'apache-arrow';\nimport {csvFormat} from 'd3-dsv';\nimport {saveAs} from 'file-saver';\nimport {produce} from 'immer';\n\nexport type QueryResult =\n | {status: 'loading'; isBeingAborted?: boolean; controller: AbortController}\n | {status: 'aborted'}\n | {status: 'error'; error: string}\n | {\n status: 'success';\n type: 'pragma' | 'explain' | 'select';\n result: arrow.Table | undefined;\n lastQueryStatement: string;\n }\n | {\n status: 'success';\n type: 'exec';\n lastQueryStatement: string;\n };\n\nexport function isQueryWithResult(\n queryResult: QueryResult | undefined,\n): queryResult is QueryResult & {\n status: 'success';\n type: 'pragma' | 'explain' | 'select';\n} {\n return (\n queryResult?.status === 'success' &&\n (queryResult.type === 'pragma' ||\n queryResult.type === 'explain' ||\n queryResult.type === 'select')\n );\n}\n\nexport type SqlEditorSliceState = {\n sqlEditor: {\n config: SqlEditorSliceConfig;\n // Runtime state\n queryResult?: QueryResult;\n /** @deprecated */\n selectedTable?: string;\n /** @deprecated Use `useStoreWithSqlEditor((s) => s.db.isRefreshingTableSchemas)` instead. */\n isTablesLoading: boolean;\n /** @deprecated */\n tablesError?: string;\n\n queryResultLimit: number;\n /** Options for the result limit dropdown */\n queryResultLimitOptions: number[];\n\n /**\n * Set the config for the sql editor slice.\n */\n setConfig(config: SqlEditorSliceConfig): void;\n\n /**\n * Run the currently selected query.\n */\n parseAndRunQuery(query: string): Promise<void>;\n\n /**\n * Run the currently selected query.\n */\n parseAndRunCurrentQuery(): Promise<void>;\n\n /**\n * Abort the currently running query.\n */\n abortCurrentQuery(): void;\n\n /**\n * Export query results to CSV.\n * @deprecated Use `useExportToCsv` from `@sqlrooms/duckdb` instead.\n */\n exportResultsToCsv(results: arrow.Table, filename?: string): void;\n\n /**\n * Create a new query tab.\n * @param initialQuery - Optional initial query text.\n */\n createQueryTab(initialQuery?: string): {\n id: string;\n name: string;\n query: string;\n };\n\n /**\n * Delete a query tab.\n * @param queryId - The ID of the query to delete.\n */\n deleteQueryTab(queryId: string): void;\n\n /**\n * Rename a query tab.\n * @param queryId - The ID of the query to rename.\n * @param newName - The new name for the query.\n */\n renameQueryTab(queryId: string, newName: string): void;\n\n /**\n * Close a query tab.\n * @param queryId - The ID of the query to close.\n */\n closeQueryTab(queryId: string): void;\n /**\n * Open a closed tab id.\n * @param queryId - The ID of the query to remove.\n */\n openQueryTab(queryId: string): void;\n\n /**\n * Update the SQL text for a query.\n * @param queryId - The ID of the query to update.\n * @param queryText - The new SQL text.\n */\n updateQueryText(queryId: string, queryText: string): void;\n\n /**\n * Set the selected query tab.\n * @param queryId - The ID of the query to select.\n */\n setSelectedQueryId(queryId: string): void;\n\n /**\n * Get the currently selected query's SQL text.\n */\n getCurrentQuery(): string;\n\n /** @deprecated */\n selectTable(table: string | undefined): void;\n\n clearQueryResults(): void;\n\n setQueryResultLimit(limit: number): void;\n };\n};\n\nexport function createSqlEditorSlice({\n config = createDefaultSqlEditorConfig(),\n queryResultLimit = 100,\n queryResultLimitOptions = [100, 500, 1000],\n}: {\n config?: SqlEditorSliceConfig;\n queryResultLimit?: number;\n queryResultLimitOptions?: number[];\n} = {}): StateCreator<SqlEditorSliceState> {\n return createSlice<\n SqlEditorSliceState,\n BaseRoomStoreState & DuckDbSliceState & SqlEditorSliceState\n >((set, get) => {\n return {\n sqlEditor: {\n config,\n // Initialize runtime state\n isTablesLoading: false,\n queryResultLimit,\n queryResultLimitOptions,\n\n setConfig: (config) => {\n set((state) =>\n produce(state, (draft) => {\n draft.sqlEditor.config = config;\n }),\n );\n },\n\n exportResultsToCsv: (results, filename) => {\n if (!results) return;\n const blob = new Blob([csvFormat(results.toArray())], {\n type: 'text/plain;charset=utf-8',\n });\n saveAs(blob, filename || `export-${createId().substring(0, 5)}.csv`);\n },\n\n createQueryTab: (initialQuery = '') => {\n const sqlEditorConfig = get().sqlEditor.config;\n const newQuery = {\n id: createId(),\n name: generateUniqueName(\n 'Untitled',\n sqlEditorConfig.queries.map((q) => q.name),\n ),\n query: initialQuery,\n };\n\n set((state) =>\n produce(state, (draft) => {\n draft.sqlEditor.config.queries.push(newQuery);\n draft.sqlEditor.config.selectedQueryId = newQuery.id;\n }),\n );\n\n return newQuery;\n },\n\n deleteQueryTab: (queryId) => {\n const sqlEditorConfig = get().sqlEditor.config;\n const queries = sqlEditorConfig.queries;\n\n if (queries.length <= 1) {\n // Don't delete the last query\n return;\n }\n\n const index = queries.findIndex((q) => q.id === queryId);\n if (index === -1) return;\n\n const isSelected = sqlEditorConfig.selectedQueryId === queryId;\n const filteredQueries = queries.filter((q) => q.id !== queryId);\n\n set((state) =>\n produce(state, (draft) => {\n draft.sqlEditor.config.queries = filteredQueries;\n\n // If we're deleting the selected tab, select the previous one or the first one\n if (isSelected && filteredQueries.length > 0) {\n const newSelectedIndex = Math.max(0, index - 1);\n // Safely access the ID with fallback to the first query if needed\n const newSelectedId =\n filteredQueries[newSelectedIndex]?.id ??\n filteredQueries[0]?.id;\n if (newSelectedId) {\n draft.sqlEditor.config.selectedQueryId = newSelectedId;\n }\n }\n }),\n );\n },\n\n renameQueryTab: (queryId, newName) => {\n set((state) =>\n produce(state, (draft) => {\n const query = draft.sqlEditor.config.queries.find(\n (q) => q.id === queryId,\n );\n if (query) {\n query.name = newName || query.name;\n }\n }),\n );\n },\n\n closeQueryTab: (queryId) => {\n set((state) =>\n produce(state, (draft) => {\n draft.sqlEditor.config.closedTabIds.push(queryId);\n const openedTabs = draft.sqlEditor.config.queries.filter(\n (q) => !draft.sqlEditor.config.closedTabIds.includes(q.id),\n );\n\n if (\n draft.sqlEditor.config.selectedQueryId === queryId &&\n openedTabs.length > 0 &&\n openedTabs[0]\n ) {\n draft.sqlEditor.config.selectedQueryId = openedTabs[0].id;\n }\n }),\n );\n },\n\n openQueryTab: (queryId) => {\n set((state) =>\n produce(state, (draft) => {\n draft.sqlEditor.config.closedTabIds =\n draft.sqlEditor.config.closedTabIds?.filter(\n (id) => id !== queryId,\n );\n draft.sqlEditor.config.selectedQueryId = queryId;\n }),\n );\n },\n\n updateQueryText: (queryId, queryText) => {\n set((state) =>\n produce(state, (draft) => {\n const query = draft.sqlEditor.config.queries.find(\n (q) => q.id === queryId,\n );\n if (query) {\n query.query = queryText;\n }\n }),\n );\n },\n\n setSelectedQueryId: (queryId) => {\n set((state) =>\n produce(state, (draft) => {\n draft.sqlEditor.config.selectedQueryId = queryId;\n }),\n );\n },\n\n getCurrentQuery: () => {\n const sqlEditorConfig = get().sqlEditor.config;\n const selectedId = sqlEditorConfig.selectedQueryId;\n const query = sqlEditorConfig.queries.find(\n (q) => q.id === selectedId,\n );\n return query?.query || '';\n },\n\n /** @deprecated */\n selectTable: (table) => {\n set((state) =>\n produce(state, (draft) => {\n draft.sqlEditor.selectedTable = table;\n }),\n );\n },\n\n clearQueryResults: () => {\n // Update state without using Immer for the Table type\n set((state) => ({\n ...state,\n sqlEditor: {\n ...state.sqlEditor,\n queryResults: null,\n queryError: undefined,\n },\n }));\n },\n\n parseAndRunCurrentQuery: async (): Promise<void> =>\n get().sqlEditor.parseAndRunQuery(get().sqlEditor.getCurrentQuery()),\n\n abortCurrentQuery: () => {\n const currentResult = get().sqlEditor.queryResult;\n if (currentResult?.status === 'loading' && currentResult.controller) {\n currentResult.controller.abort();\n }\n\n set((state) =>\n produce(state, (draft) => {\n if (draft.sqlEditor.queryResult?.status === 'loading') {\n draft.sqlEditor.queryResult.isBeingAborted = true;\n }\n }),\n );\n },\n\n setQueryResultLimit: (limit) => {\n set((state) =>\n produce(state, (draft) => {\n draft.sqlEditor.queryResultLimit = limit;\n }),\n );\n },\n\n parseAndRunQuery: async (query): Promise<void> => {\n if (get().sqlEditor.queryResult?.status === 'loading') {\n throw new Error('Query already running');\n }\n if (!query.trim()) {\n return;\n }\n\n // Create abort controller for this query execution\n const queryController = new AbortController();\n\n // First update loading state and clear results\n set((state) =>\n produce(state, (draft) => {\n draft.sqlEditor.selectedTable = undefined;\n draft.sqlEditor.queryResult = {\n status: 'loading',\n isBeingAborted: false,\n controller: queryController,\n };\n }),\n );\n\n let queryResult: QueryResult;\n try {\n const connector = await get().db.getConnector();\n const signal = queryController.signal;\n\n const statements = splitSqlStatements(query);\n const allButLastStatements = statements.slice(0, -1);\n const lastQueryStatement = statements[\n statements.length - 1\n ] as string;\n\n if (!statements?.length) {\n throw new Error('Empty query');\n }\n\n if (signal.aborted) {\n throw new Error('Query aborted');\n }\n\n const parsedLastStatement =\n await get().db.sqlSelectToJson(lastQueryStatement);\n\n if (signal.aborted) {\n throw new Error('Query aborted');\n }\n\n const isValidSelectQuery = !parsedLastStatement.error;\n\n if (isValidSelectQuery) {\n // Add limit to the last statement\n const queryWithLimit = [\n ...allButLastStatements,\n makeLimitQuery(lastQueryStatement, {\n sanitize: false, // should already be sanitized\n limit: get().sqlEditor.queryResultLimit,\n }),\n ].join(';\\n');\n const result = await connector.query(queryWithLimit, {signal});\n queryResult = {\n status: 'success',\n type: 'select',\n lastQueryStatement,\n result,\n };\n } else {\n // Run the complete query as it is\n if (\n parsedLastStatement.error &&\n parsedLastStatement.error_type !== 'not implemented'\n ) {\n throw (\n `${parsedLastStatement.error_type} ${parsedLastStatement.error_subtype}: ${parsedLastStatement.error_message}` +\n `\\n${getSqlErrorWithPointer(lastQueryStatement, Number(parsedLastStatement.position)).formatted}`\n );\n }\n\n const result = await connector.query(query, {signal});\n // EXPLAIN and PRAGMA are not detected as select queries\n // and we cannot wrap them in a SELECT * FROM,\n // but we can still execute them and return the result\n if (/^(EXPLAIN)/i.test(lastQueryStatement)) {\n queryResult = {\n status: 'success',\n type: 'explain',\n lastQueryStatement,\n result,\n };\n } else if (/^(PRAGMA)/i.test(lastQueryStatement)) {\n queryResult = {\n status: 'success',\n type: 'pragma',\n lastQueryStatement,\n result,\n };\n } else {\n queryResult = {\n status: 'success',\n type: 'exec',\n lastQueryStatement,\n };\n }\n }\n if (signal.aborted) {\n throw new Error('Query aborted');\n }\n // Refresh table schemas if there are multiple statements or if the\n // last statement is not a select query\n if (statements.length > 1 || !isValidSelectQuery) {\n get().db.refreshTableSchemas();\n }\n if (signal.aborted) {\n throw new Error('Query aborted');\n }\n } catch (e) {\n console.error(e);\n const errorMessage = e instanceof Error ? e.message : String(e);\n if (\n errorMessage === 'Query aborted' ||\n queryController.signal.aborted\n ) {\n queryResult = {status: 'aborted'};\n } else {\n queryResult = {\n status: 'error',\n error: errorMessage,\n };\n }\n }\n\n set((state) => ({\n ...state,\n sqlEditor: {...state.sqlEditor, queryResult},\n }));\n },\n },\n } satisfies SqlEditorSliceState;\n });\n}\n\ntype RoomStateWithSqlEditor = RoomShellSliceState & SqlEditorSliceState;\n\nexport function useStoreWithSqlEditor<T>(\n selector: (state: RoomStateWithSqlEditor) => T,\n): T {\n return useBaseRoomShellStore<RoomShellSliceState, T>((state) =>\n selector(state as unknown as RoomStateWithSqlEditor),\n );\n}\n"]}
1
+ {"version":3,"file":"SqlEditorSlice.js","sourceRoot":"","sources":["../src/SqlEditorSlice.tsx"],"names":[],"mappings":"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","sourcesContent":["import {createId} from '@paralleldrive/cuid2';\nimport {\n DuckDbSliceState,\n getSqlErrorWithPointer,\n joinStatements,\n makeLimitQuery,\n separateLastStatement,\n} from '@sqlrooms/duckdb';\nimport {\n BaseRoomStoreState,\n createSlice,\n RoomShellSliceState,\n StateCreator,\n useBaseRoomShellStore,\n} from '@sqlrooms/room-shell';\nimport {\n createDefaultSqlEditorConfig,\n SqlEditorSliceConfig,\n} from '@sqlrooms/sql-editor-config';\nimport {generateUniqueName} from '@sqlrooms/utils';\nimport * as arrow from 'apache-arrow';\nimport {csvFormat} from 'd3-dsv';\nimport {saveAs} from 'file-saver';\nimport {produce} from 'immer';\n\nexport type QueryResult =\n | {status: 'loading'; isBeingAborted?: boolean; controller: AbortController}\n | {status: 'aborted'}\n | {status: 'error'; error: string}\n | {\n status: 'success';\n type: 'pragma' | 'explain' | 'select';\n result: arrow.Table | undefined;\n query: string;\n lastQueryStatement: string;\n }\n | {\n status: 'success';\n type: 'exec';\n query: string;\n lastQueryStatement: string;\n };\n\nexport function isQueryWithResult(\n queryResult: QueryResult | undefined,\n): queryResult is QueryResult & {\n status: 'success';\n type: 'pragma' | 'explain' | 'select';\n} {\n return (\n queryResult?.status === 'success' &&\n (queryResult.type === 'pragma' ||\n queryResult.type === 'explain' ||\n queryResult.type === 'select')\n );\n}\n\nexport type SqlEditorSliceState = {\n sqlEditor: {\n config: SqlEditorSliceConfig;\n // Runtime state\n queryResult?: QueryResult;\n /** @deprecated */\n selectedTable?: string;\n /** @deprecated Use `useStoreWithSqlEditor((s) => s.db.isRefreshingTableSchemas)` instead. */\n isTablesLoading: boolean;\n /** @deprecated */\n tablesError?: string;\n\n queryResultLimit: number;\n /** Options for the result limit dropdown */\n queryResultLimitOptions: number[];\n\n /**\n * Set the config for the sql editor slice.\n */\n setConfig(config: SqlEditorSliceConfig): void;\n\n /**\n * Run the currently selected query.\n */\n parseAndRunQuery(query: string): Promise<void>;\n\n /**\n * Run the currently selected query.\n */\n parseAndRunCurrentQuery(): Promise<void>;\n\n /**\n * Abort the currently running query.\n */\n abortCurrentQuery(): void;\n\n /**\n * Export query results to CSV.\n * @deprecated Use `useExportToCsv` from `@sqlrooms/duckdb` instead.\n */\n exportResultsToCsv(results: arrow.Table, filename?: string): void;\n\n /**\n * Create a new query tab.\n * @param initialQuery - Optional initial query text.\n */\n createQueryTab(initialQuery?: string): {\n id: string;\n name: string;\n query: string;\n };\n\n /**\n * Delete a query tab.\n * @param queryId - The ID of the query to delete.\n */\n deleteQueryTab(queryId: string): void;\n\n /**\n * Rename a query tab.\n * @param queryId - The ID of the query to rename.\n * @param newName - The new name for the query.\n */\n renameQueryTab(queryId: string, newName: string): void;\n\n /**\n * Close a query tab.\n * @param queryId - The ID of the query to close.\n */\n closeQueryTab(queryId: string): void;\n\n /**\n * Open a closed tab id.\n * @param queryId - The ID of the query to open.\n */\n openQueryTab(queryId: string): void;\n\n /**\n * Set the list of open tab IDs. Used for reordering or opening tabs.\n * @param tabIds - The new list of open tab IDs.\n */\n setOpenTabs(tabIds: string[]): void;\n\n /**\n * Update the SQL text for a query.\n * @param queryId - The ID of the query to update.\n * @param queryText - The new SQL text.\n */\n updateQueryText(queryId: string, queryText: string): void;\n\n /**\n * Set the selected query tab.\n * @param queryId - The ID of the query to select.\n */\n setSelectedQueryId(queryId: string): void;\n\n /**\n * Get the currently selected query's SQL text.\n */\n getCurrentQuery(): string;\n\n /** @deprecated */\n selectTable(table: string | undefined): void;\n\n clearQueryResults(): void;\n\n setQueryResultLimit(limit: number): void;\n };\n};\n\nexport function createSqlEditorSlice({\n config = createDefaultSqlEditorConfig(),\n queryResultLimit = 100,\n queryResultLimitOptions = [100, 500, 1000],\n}: {\n config?: SqlEditorSliceConfig;\n queryResultLimit?: number;\n queryResultLimitOptions?: number[];\n} = {}): StateCreator<SqlEditorSliceState> {\n return createSlice<\n SqlEditorSliceState,\n BaseRoomStoreState & DuckDbSliceState & SqlEditorSliceState\n >((set, get) => {\n return {\n sqlEditor: {\n config,\n // Initialize runtime state\n isTablesLoading: false,\n queryResultLimit,\n queryResultLimitOptions,\n\n setConfig: (config) => {\n set((state) =>\n produce(state, (draft) => {\n draft.sqlEditor.config = config;\n }),\n );\n },\n\n exportResultsToCsv: (results, filename) => {\n if (!results) return;\n const blob = new Blob([csvFormat(results.toArray())], {\n type: 'text/plain;charset=utf-8',\n });\n saveAs(blob, filename || `export-${createId().substring(0, 5)}.csv`);\n },\n\n createQueryTab: (initialQuery = '') => {\n const sqlEditorConfig = get().sqlEditor.config;\n const newQuery = {\n id: createId(),\n name: generateUniqueName(\n 'Untitled',\n sqlEditorConfig.queries.map((q) => q.name),\n ),\n query: initialQuery,\n };\n\n set((state) =>\n produce(state, (draft) => {\n draft.sqlEditor.config.queries.push(newQuery);\n draft.sqlEditor.config.openTabs.push(newQuery.id);\n draft.sqlEditor.config.selectedQueryId = newQuery.id;\n }),\n );\n\n return newQuery;\n },\n\n deleteQueryTab: (queryId) => {\n const sqlEditorConfig = get().sqlEditor.config;\n const queries = sqlEditorConfig.queries;\n const openTabs = sqlEditorConfig.openTabs;\n\n if (queries.length <= 1) {\n // Don't delete the last query\n return;\n }\n\n const wasSelected = sqlEditorConfig.selectedQueryId === queryId;\n const deletingOpenIndex = openTabs.indexOf(queryId);\n const filteredQueries = queries.filter((q) => q.id !== queryId);\n\n set((state) =>\n produce(state, (draft) => {\n draft.sqlEditor.config.queries = filteredQueries;\n draft.sqlEditor.config.openTabs = openTabs.filter(\n (id) => id !== queryId,\n );\n\n // If we deleted the selected query, select another one\n if (wasSelected) {\n const newOpenTabs = draft.sqlEditor.config.openTabs;\n const remainingQueries = draft.sqlEditor.config.queries;\n\n if (newOpenTabs.length > 0) {\n // Select from remaining open tabs\n const newIndex =\n deletingOpenIndex === 0\n ? 0\n : Math.min(deletingOpenIndex - 1, newOpenTabs.length - 1);\n const newSelectedId = newOpenTabs[newIndex];\n if (newSelectedId) {\n draft.sqlEditor.config.selectedQueryId = newSelectedId;\n }\n } else if (remainingQueries.length > 0) {\n // No open tabs left, open a closed query\n const queryToOpen = remainingQueries[0];\n if (queryToOpen) {\n draft.sqlEditor.config.openTabs.push(queryToOpen.id);\n draft.sqlEditor.config.selectedQueryId = queryToOpen.id;\n }\n }\n }\n }),\n );\n },\n\n renameQueryTab: (queryId, newName) => {\n set((state) =>\n produce(state, (draft) => {\n const query = draft.sqlEditor.config.queries.find(\n (q) => q.id === queryId,\n );\n if (query) {\n query.name = newName || query.name;\n }\n }),\n );\n },\n\n closeQueryTab: (queryId) => {\n set((state) =>\n produce(state, (draft) => {\n draft.sqlEditor.config.openTabs =\n draft.sqlEditor.config.openTabs.filter((id) => id !== queryId);\n }),\n );\n },\n\n openQueryTab: (queryId) => {\n set((state) =>\n produce(state, (draft) => {\n if (!draft.sqlEditor.config.openTabs.includes(queryId)) {\n draft.sqlEditor.config.openTabs.push(queryId);\n }\n draft.sqlEditor.config.selectedQueryId = queryId;\n }),\n );\n },\n\n setOpenTabs: (tabIds) => {\n set((state) =>\n produce(state, (draft) => {\n draft.sqlEditor.config.openTabs = tabIds;\n }),\n );\n },\n\n updateQueryText: (queryId, queryText) => {\n set((state) =>\n produce(state, (draft) => {\n const query = draft.sqlEditor.config.queries.find(\n (q) => q.id === queryId,\n );\n if (query) {\n query.query = queryText;\n }\n }),\n );\n },\n\n setSelectedQueryId: (queryId) => {\n set((state) =>\n produce(state, (draft) => {\n draft.sqlEditor.config.selectedQueryId = queryId;\n }),\n );\n },\n\n getCurrentQuery: () => {\n const sqlEditorConfig = get().sqlEditor.config;\n const selectedId = sqlEditorConfig.selectedQueryId;\n const query = sqlEditorConfig.queries.find(\n (q) => q.id === selectedId,\n );\n return query?.query || '';\n },\n\n /** @deprecated */\n selectTable: (table) => {\n set((state) =>\n produce(state, (draft) => {\n draft.sqlEditor.selectedTable = table;\n }),\n );\n },\n\n clearQueryResults: () => {\n // Update state without using Immer for the Table type\n set((state) => ({\n ...state,\n sqlEditor: {\n ...state.sqlEditor,\n queryResults: null,\n queryError: undefined,\n },\n }));\n },\n\n parseAndRunCurrentQuery: async (): Promise<void> =>\n get().sqlEditor.parseAndRunQuery(get().sqlEditor.getCurrentQuery()),\n\n abortCurrentQuery: () => {\n const currentResult = get().sqlEditor.queryResult;\n if (currentResult?.status === 'loading' && currentResult.controller) {\n currentResult.controller.abort();\n }\n\n set((state) =>\n produce(state, (draft) => {\n if (draft.sqlEditor.queryResult?.status === 'loading') {\n draft.sqlEditor.queryResult.isBeingAborted = true;\n }\n }),\n );\n },\n\n setQueryResultLimit: (limit) => {\n set((state) =>\n produce(state, (draft) => {\n draft.sqlEditor.queryResultLimit = limit;\n }),\n );\n },\n\n parseAndRunQuery: async (query): Promise<void> => {\n if (get().sqlEditor.queryResult?.status === 'loading') {\n throw new Error('Query already running');\n }\n if (!query.trim()) {\n return;\n }\n\n // Create abort controller for this query execution\n const queryController = new AbortController();\n\n // First update loading state and clear results\n set((state) =>\n produce(state, (draft) => {\n draft.sqlEditor.selectedTable = undefined;\n draft.sqlEditor.queryResult = {\n status: 'loading',\n isBeingAborted: false,\n controller: queryController,\n };\n }),\n );\n\n let queryResult: QueryResult;\n try {\n const connector = await get().db.getConnector();\n const signal = queryController.signal;\n\n const {precedingStatements, lastStatement: lastQueryStatement} =\n separateLastStatement(query);\n const hasMultipleStatements = precedingStatements.length > 0;\n\n if (signal.aborted) {\n throw new Error('Query aborted');\n }\n\n const parsedLastStatement =\n await get().db.sqlSelectToJson(lastQueryStatement);\n\n if (signal.aborted) {\n throw new Error('Query aborted');\n }\n\n const isValidSelectQuery = !parsedLastStatement.error;\n\n if (isValidSelectQuery) {\n // Add limit to the last statement\n const limitedLastStatement = makeLimitQuery(lastQueryStatement, {\n sanitize: false, // should already be sanitized\n limit: get().sqlEditor.queryResultLimit,\n });\n const queryWithLimit = joinStatements(\n precedingStatements,\n limitedLastStatement,\n );\n const result = await connector.query(queryWithLimit, {signal});\n queryResult = {\n status: 'success',\n type: 'select',\n query,\n lastQueryStatement,\n result,\n };\n } else {\n // Run the complete query as it is\n if (\n parsedLastStatement.error &&\n parsedLastStatement.error_type !== 'not implemented'\n ) {\n throw (\n `${parsedLastStatement.error_type} ${parsedLastStatement.error_subtype}: ${parsedLastStatement.error_message}` +\n `\\n${getSqlErrorWithPointer(lastQueryStatement, Number(parsedLastStatement.position)).formatted}`\n );\n }\n\n const result = await connector.query(query, {signal});\n // EXPLAIN and PRAGMA are not detected as select queries\n // and we cannot wrap them in a SELECT * FROM,\n // but we can still execute them and return the result\n if (/^(EXPLAIN)/i.test(lastQueryStatement)) {\n queryResult = {\n status: 'success',\n type: 'explain',\n query,\n lastQueryStatement,\n result,\n };\n } else if (/^(PRAGMA)/i.test(lastQueryStatement)) {\n queryResult = {\n status: 'success',\n type: 'pragma',\n query,\n lastQueryStatement,\n result,\n };\n } else {\n queryResult = {\n status: 'success',\n type: 'exec',\n query,\n lastQueryStatement,\n };\n }\n }\n if (signal.aborted) {\n throw new Error('Query aborted');\n }\n // Refresh table schemas if there are multiple statements or if the\n // last statement is not a select query\n if (hasMultipleStatements || !isValidSelectQuery) {\n get().db.refreshTableSchemas();\n }\n if (signal.aborted) {\n throw new Error('Query aborted');\n }\n } catch (e) {\n console.error(e);\n const errorMessage = e instanceof Error ? e.message : String(e);\n if (\n errorMessage === 'Query aborted' ||\n queryController.signal.aborted\n ) {\n queryResult = {status: 'aborted'};\n } else {\n queryResult = {\n status: 'error',\n error: errorMessage,\n };\n }\n }\n\n set((state) => ({\n ...state,\n sqlEditor: {...state.sqlEditor, queryResult},\n }));\n },\n },\n } satisfies SqlEditorSliceState;\n });\n}\n\ntype RoomStateWithSqlEditor = RoomShellSliceState & SqlEditorSliceState;\n\nexport function useStoreWithSqlEditor<T>(\n selector: (state: RoomStateWithSqlEditor) => T,\n): T {\n return useBaseRoomShellStore<RoomShellSliceState, T>((state) =>\n selector(state as unknown as RoomStateWithSqlEditor),\n );\n}\n"]}
@@ -1,11 +1,44 @@
1
1
  import { SqlQueryDataSource } from '@sqlrooms/room-shell';
2
2
  import { FC } from 'react';
3
+ /**
4
+ * Initial values for the create table form.
5
+ */
6
+ export type CreateTableFormInitialValues = Partial<{
7
+ tableName: string;
8
+ replace: boolean;
9
+ temp: boolean;
10
+ view: boolean;
11
+ schema: string;
12
+ database: string;
13
+ }>;
3
14
  export type CreateTableModalProps = {
4
15
  query: string;
5
16
  isOpen: boolean;
6
17
  onClose: () => void;
7
18
  editDataSource?: SqlQueryDataSource;
8
- onAddOrUpdateSqlQuery: (tableName: string, query: string, oldTableName?: string) => Promise<void>;
19
+ /**
20
+ * Allow multiple statements in the query. When true, preceding statements
21
+ * will be executed before the final SELECT is wrapped in CREATE TABLE/VIEW.
22
+ */
23
+ allowMultipleStatements?: boolean;
24
+ /**
25
+ * Show schema/database selection UI.
26
+ * @default false
27
+ */
28
+ showSchemaSelection?: boolean;
29
+ /**
30
+ * @deprecated Use createTableFromQuery directly instead.
31
+ * When not provided, the modal will call createTableFromQuery directly.
32
+ */
33
+ onAddOrUpdateSqlQuery?: (tableName: string, query: string, oldTableName?: string) => Promise<void>;
34
+ /**
35
+ * Additional class name for the dialog content.
36
+ */
37
+ className?: string;
38
+ /**
39
+ * Initial values for the form fields.
40
+ */
41
+ initialValues?: CreateTableFormInitialValues;
9
42
  };
10
43
  declare const CreateTableModal: FC<CreateTableModalProps>;
11
44
  export default CreateTableModal;
@@ -1 +1 @@
1
- {"version":3,"file":"CreateTableModal.d.ts","sourceRoot":"","sources":["../../src/components/CreateTableModal.tsx"],"names":[],"mappings":"AACA,OAAO,EAAC,kBAAkB,EAAC,MAAM,sBAAsB,CAAC;AAmBxD,OAAO,EAAC,EAAE,EAAc,MAAM,OAAO,CAAC;AAmBtC,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,cAAc,CAAC,EAAE,kBAAkB,CAAC;IACpC,qBAAqB,EAAE,CACrB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,YAAY,CAAC,EAAE,MAAM,KAClB,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB,CAAC;AAyHF,QAAA,MAAM,gBAAgB,EAAE,EAAE,CAAC,qBAAqB,CAiB/C,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"CreateTableModal.d.ts","sourceRoot":"","sources":["../../src/components/CreateTableModal.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAC,kBAAkB,EAAC,MAAM,sBAAsB,CAAC;AAqCxD,OAAO,EAAC,EAAE,EAAiC,MAAM,OAAO,CAAC;AA0BzD;;GAEG;AACH,MAAM,MAAM,4BAA4B,GAAG,OAAO,CAAC;IACjD,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC,CAAC;AAEH,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,cAAc,CAAC,EAAE,kBAAkB,CAAC;IACpC;;;OAGG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC;;;OAGG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;;;OAGG;IACH,qBAAqB,CAAC,EAAE,CACtB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,YAAY,CAAC,EAAE,MAAM,KAClB,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,aAAa,CAAC,EAAE,4BAA4B,CAAC;CAC9C,CAAC;AAybF,QAAA,MAAM,gBAAgB,EAAE,EAAE,CAAC,qBAAqB,CA8B/C,CAAC;AAEF,eAAe,gBAAgB,CAAC"}