From 91ffb5ce54a40fe2fca567cfaf3bdb86b708f897 Mon Sep 17 00:00:00 2001
From: Diego Imbert <70353967+diegoimbert@users.noreply.github.com>
Date: Thu, 12 Feb 2026 19:45:07 +0100
Subject: [PATCH] feat: support for datatables in App Db studio (#7930)
---
.../src/lib/components/DatatablePicker.svelte | 55 +++++++++++++++++++
.../display/dbtable/AppDbExplorer.svelte | 30 ++++++----
.../display/dbtable/queries/count.ts | 4 +-
.../display/dbtable/queries/delete.ts | 4 +-
.../display/dbtable/queries/insert.ts | 4 +-
.../display/dbtable/queries/select.ts | 4 +-
.../display/dbtable/queries/update.ts | 4 +-
.../apps/editor/component/components.ts | 18 +++++-
.../inputEditor/StaticInputEditor.svelte | 10 ++++
frontend/src/lib/components/apps/inputType.ts | 2 +
10 files changed, 119 insertions(+), 16 deletions(-)
create mode 100644 frontend/src/lib/components/DatatablePicker.svelte
diff --git a/frontend/src/lib/components/DatatablePicker.svelte b/frontend/src/lib/components/DatatablePicker.svelte
new file mode 100644
index 0000000000..68cdccea63
--- /dev/null
+++ b/frontend/src/lib/components/DatatablePicker.svelte
@@ -0,0 +1,55 @@
+
+
+
+
diff --git a/frontend/src/lib/components/apps/components/display/dbtable/AppDbExplorer.svelte b/frontend/src/lib/components/apps/components/display/dbtable/AppDbExplorer.svelte
index 4661a1fb15..e7d239bc8c 100644
--- a/frontend/src/lib/components/apps/components/display/dbtable/AppDbExplorer.svelte
+++ b/frontend/src/lib/components/apps/components/display/dbtable/AppDbExplorer.svelte
@@ -128,9 +128,11 @@
let buttonContainerHeight: number | undefined = $state(undefined)
let dbPath = $derived(
- resolvedConfig.type.selected !== 'ducklake'
- ? resolvedConfig.type.configuration?.[resolvedConfig.type.selected]?.resource
- : resolvedConfig.type.configuration?.[resolvedConfig.type.selected]?.ducklake
+ resolvedConfig.type.selected === 'ducklake'
+ ? resolvedConfig.type.configuration?.[resolvedConfig.type.selected]?.ducklake
+ : resolvedConfig.type.selected === 'datatable'
+ ? resolvedConfig.type.configuration?.[resolvedConfig.type.selected]?.datatable
+ : resolvedConfig.type.configuration?.[resolvedConfig.type.selected]?.resource
)
let dbInput: DbInput = $derived(
resolvedConfig.type.selected === 'ducklake'
@@ -140,12 +142,19 @@
}
: {
type: 'database',
- resourcePath: dbPath.split('$res:')[1],
- resourceType: resolvedConfig.type.selected as DbType
+ resourcePath: dbPath.split('$res:')[1] ?? dbPath,
+ resourceType:
+ resolvedConfig.type.selected === 'datatable'
+ ? 'postgresql'
+ : (resolvedConfig.type.selected as DbType)
}
)
let dbtype = $derived(
- resolvedConfig.type.selected === 'ducklake' ? ('duckdb' as const) : resolvedConfig.type.selected
+ resolvedConfig.type.selected === 'ducklake'
+ ? ('duckdb' as const)
+ : resolvedConfig.type.selected === 'datatable'
+ ? 'postgresql'
+ : resolvedConfig.type.selected
)
function onUpdate(
@@ -234,9 +243,11 @@
ducklake: dbPath.split('ducklake://')[1]
})
} else {
- const resourcePath = dbPath.split('$res:')[1]
+ const resourcePath = dbPath.split('$res:')[1] ?? dbPath
dbSchemas[resourcePath] = await getDbSchemas(
- resolvedConfig?.type?.selected,
+ resolvedConfig?.type?.selected === 'datatable'
+ ? 'postgresql'
+ : resolvedConfig?.type?.selected,
resourcePath,
$workspaceStore,
() => {},
@@ -376,7 +387,7 @@
}
: {
type: 'database',
- resourcePath: dbPath.split('$res:')[1],
+ resourcePath: dbPath.split('$res:')[1] ?? dbPath,
resourceType: dbtype
},
$workspaceStore,
@@ -508,7 +519,6 @@
async function insert(args: object) {
try {
const selected = resolvedConfig.type.selected
-
await insertRowRunnable?.insertRow(
dbInput,
$workspaceStore,
diff --git a/frontend/src/lib/components/apps/components/display/dbtable/queries/count.ts b/frontend/src/lib/components/apps/components/display/dbtable/queries/count.ts
index b418c1a702..3a7bf66a62 100644
--- a/frontend/src/lib/components/apps/components/display/dbtable/queries/count.ts
+++ b/frontend/src/lib/components/apps/components/display/dbtable/queries/count.ts
@@ -191,7 +191,9 @@ export function getCountInput(
? {
database: {
type: 'static',
- value: `$res:${dbInput.resourcePath}`,
+ value: dbInput.resourcePath.startsWith('datatable://')
+ ? dbInput.resourcePath
+ : `$res:${dbInput.resourcePath}`,
fieldType: 'object',
format: `resource-${dbType}`
}
diff --git a/frontend/src/lib/components/apps/components/display/dbtable/queries/delete.ts b/frontend/src/lib/components/apps/components/display/dbtable/queries/delete.ts
index 3edd847db4..a6ded0f727 100644
--- a/frontend/src/lib/components/apps/components/display/dbtable/queries/delete.ts
+++ b/frontend/src/lib/components/apps/components/display/dbtable/queries/delete.ts
@@ -103,7 +103,9 @@ export function getDeleteInput(
? {
database: {
type: 'static',
- value: `$res:${dbInput.resourcePath}`,
+ value: dbInput.resourcePath.startsWith('datatable://')
+ ? dbInput.resourcePath
+ : `$res:${dbInput.resourcePath}`,
fieldType: 'object',
format: `resource-${dbType}`
}
diff --git a/frontend/src/lib/components/apps/components/display/dbtable/queries/insert.ts b/frontend/src/lib/components/apps/components/display/dbtable/queries/insert.ts
index fd1df5fe2b..9983b7c8e8 100644
--- a/frontend/src/lib/components/apps/components/display/dbtable/queries/insert.ts
+++ b/frontend/src/lib/components/apps/components/display/dbtable/queries/insert.ts
@@ -130,7 +130,9 @@ export function getInsertInput(dbInput: DbInput, table: string, columns: ColumnD
? {
database: {
type: 'static',
- value: `$res:${dbInput.resourcePath}`,
+ value: dbInput.resourcePath.startsWith('datatable://')
+ ? dbInput.resourcePath
+ : `$res:${dbInput.resourcePath}`,
fieldType: 'object',
format: `resource-${dbType}`
}
diff --git a/frontend/src/lib/components/apps/components/display/dbtable/queries/select.ts b/frontend/src/lib/components/apps/components/display/dbtable/queries/select.ts
index 942dda6079..9dc5801c7e 100644
--- a/frontend/src/lib/components/apps/components/display/dbtable/queries/select.ts
+++ b/frontend/src/lib/components/apps/components/display/dbtable/queries/select.ts
@@ -337,7 +337,9 @@ export function getSelectInput(
? {
database: {
type: 'static',
- value: `$res:${dbInput.resourcePath}`,
+ value: dbInput.resourcePath.startsWith('datatable://')
+ ? dbInput.resourcePath
+ : `$res:${dbInput.resourcePath}`,
fieldType: 'object',
format: `resource-${dbType}`
}
diff --git a/frontend/src/lib/components/apps/components/display/dbtable/queries/update.ts b/frontend/src/lib/components/apps/components/display/dbtable/queries/update.ts
index a6007dd4c9..53aad833d4 100644
--- a/frontend/src/lib/components/apps/components/display/dbtable/queries/update.ts
+++ b/frontend/src/lib/components/apps/components/display/dbtable/queries/update.ts
@@ -115,7 +115,9 @@ export function getUpdateInput(
? {
database: {
type: 'static',
- value: `$res:${dbInput.resourcePath}`,
+ value: dbInput.resourcePath.startsWith('datatable://')
+ ? dbInput.resourcePath
+ : `$res:${dbInput.resourcePath}`,
fieldType: 'object',
format: `resource-${dbType}`
}
diff --git a/frontend/src/lib/components/apps/editor/component/components.ts b/frontend/src/lib/components/apps/editor/component/components.ts
index 0d11e5286d..7cba68708e 100644
--- a/frontend/src/lib/components/apps/editor/component/components.ts
+++ b/frontend/src/lib/components/apps/editor/component/components.ts
@@ -4158,7 +4158,8 @@ See date-fns format for more information. By default, it is 'dd.MM.yyyy HH:mm'
snowflake: 'Snowflake',
bigquery: 'BigQuery',
snowflake_oauth: 'Snowflake OAuth',
- ducklake: 'Ducklake'
+ ducklake: 'Ducklake',
+ datatable: 'Data table'
},
configuration: {
postgresql: {
@@ -4252,6 +4253,21 @@ See date-fns format for more information. By default, it is 'dd.MM.yyyy HH:mm'
selectOptions: [],
value: undefined
}
+ },
+ datatable: {
+ datatable: {
+ type: 'static',
+ fieldType: 'datatable',
+ subFieldType: 'datatable',
+ value: ''
+ } as StaticAppInput,
+ table: {
+ fieldType: 'select',
+ subFieldType: 'db-table',
+ type: 'static',
+ selectOptions: [],
+ value: undefined
+ }
}
}
} as const,
diff --git a/frontend/src/lib/components/apps/editor/settingsPanel/inputEditor/StaticInputEditor.svelte b/frontend/src/lib/components/apps/editor/settingsPanel/inputEditor/StaticInputEditor.svelte
index 41597114c8..927f6a2712 100644
--- a/frontend/src/lib/components/apps/editor/settingsPanel/inputEditor/StaticInputEditor.svelte
+++ b/frontend/src/lib/components/apps/editor/settingsPanel/inputEditor/StaticInputEditor.svelte
@@ -30,6 +30,7 @@
import FileUpload from '$lib/components/common/fileUpload/FileUpload.svelte'
import DucklakePicker from '$lib/components/DucklakePicker.svelte'
import type SimpleEditor from '$lib/components/SimpleEditor.svelte'
+ import DatatablePicker from '$lib/components/DatatablePicker.svelte'
interface Props {
componentInput: StaticInput | undefined
@@ -159,6 +160,15 @@
}
showSchemaExplorer
/>
+ {:else if fieldType === 'datatable'}
+ componentInput?.value?.split('datatable://')?.[1],
+ (v) => componentInput && (componentInput.value = v ? `datatable://${v}` : undefined)
+ }
+ showSchemaExplorer
+ />
{:else if fieldType === 'labeledresource'}
{#if componentInput?.value && typeof componentInput?.value == 'object' && 'label' in componentInput?.value && (componentInput.value?.['value'] == undefined || typeof componentInput.value?.['value'] == 'string')}
diff --git a/frontend/src/lib/components/apps/inputType.ts b/frontend/src/lib/components/apps/inputType.ts
index 2743c46df9..a3c68a49ad 100644
--- a/frontend/src/lib/components/apps/inputType.ts
+++ b/frontend/src/lib/components/apps/inputType.ts
@@ -43,6 +43,7 @@ export type InputType =
| 'bigquery'
| 'oracledb'
| 'ducklake'
+ | 'datatable'
| 'app-path'
// Connection to an output of another component
@@ -270,6 +271,7 @@ export type AppInput =
| AppInputSpec<'resource', string, 'bigquery'>
| AppInputSpec<'resource', string, 'oracledb'>
| AppInputSpec<'ducklake', string, 'ducklake'>
+ | AppInputSpec<'datatable', string, 'datatable'>
| AppInputSpec<'array', object[], 'number-tuple'>
| AppInputSpec<'app-path', string>