opened, (v) => { if (!v) requestClose() else opened = v } } target={modalTarget} formStyling title="Add a data table" contentClasses="flex flex-col" fixedWidth="md" fixedHeight="lg" >
goToStep(e.detail.index)} />
{#if run.steps.length} {#if run.running}

Setting up. This can take a few minutes — leave this open until it finishes.

{/if} {#if run.result} {@render poolerWarning()} {/if} {:else if wiz.step === 1} A data table runs on a database of your own. It stays yours — you can take it with you at any time.
{#if $isCustomInstanceDbEnabled} {#snippet instanceIcon()} {/snippet} {@render providerCard( 'instance', instanceIcon, 'Windmill database', 'Windmill creates and manages a database on this instance.' )} {/if} {#if supabaseAvailable} {#snippet supabaseIcon()} {/snippet} {@render providerCard( 'supabase', supabaseIcon, 'Supabase', 'Create a project, or connect one you already have. Signing in is required, and connecting an existing project needs its database password.' )} {/if} {#snippet ownIcon()} {/snippet} {@render providerCard( 'resource', ownIcon, 'Your own database', 'Any Postgres — RDS, Neon, self-hosted. Pick a resource, or paste a connection string.' )}
{:else if wiz.step === 2} {#if wiz.provider === 'supabase'} {#if !supaOauth.authed} {#if supaOauth.pending} Sign in and approve Windmill in the Supabase window, then come back here. {:else} Windmill needs your approval on Supabase to see your databases. {/if} {:else} invalidate()} /> {/if} {:else if wiz.provider === 'instance'} {@render instanceStep()} {:else} {@render ownStep()} {/if} {:else}
{@render reviewStep()}
{/if}
{#if wiz.step > 1 && !run.steps.length} {:else if canEditAfterFailure} {/if}
{#if wiz.provider === 'supabase' && !supaOauth.authed}

If you do not have a Supabase account you can create one for free.

{/if}
{#snippet providerCard(key: Provider, icon: Snippet, title: string, subtitle: string)} {@const selected = wiz.provider === key} {/snippet} {#snippet instanceStep()} {@const instanceDbs = Object.entries(customInstanceDbs.current ?? {}) .filter(([_, db]) => db.tag === 'datatable') .map(([name, db]) => ({ name, db }))} {#if instanceDbs.length} wiz.instance.mode, (v) => { wiz.instance.mode = v wiz.instance.dbName = v === 'create' ? defaultInstanceDbName() : undefined } } > {#snippet children({ item })} {/snippet} {/if} {#if wiz.instance.mode === 'existing'} {@const shared = ( customInstanceDbs.current?.[wiz.instance.dbName ?? '']?.used_by_workspaces ?? [] ).filter((w) => w !== targetWorkspace)} {#if shared.length} This database is also used by workspace{shared.length > 1 ? 's' : ''} {shared.join(', ')}. Any data written here will be shared with {shared.length > 1 ? 'them' : 'it'}. {/if}
{#each instanceDbs as { name, db } (name)} {@const selected = wiz.instance.dbName === name} {@const others = (db.used_by_workspaces ?? []).filter((w) => w !== targetWorkspace)} {/each}
{:else}
Database name wiz.instance.dbName ?? '', (v) => (wiz.instance.dbName = v)} error={!!instanceNameError} inputProps={{ placeholder: defaultInstanceDbName() }} /> {#if !instanceNameError}

Created in the Windmill PostgreSQL instance when you finish. Windmill manages its credentials.

{/if}
{/if} {/snippet} {#snippet ownStep()} {@const resources = pgResources.loading ? undefined : pgResources.current} {#if resources === undefined}

Loading resources...

{:else} {#if resources.length} Postgres resources in this workspace {:else}

A resource is a saved connection your scripts can use.

{/if}
{#each resources as r (r.path)} {@const selected = !wiz.own.creating && wiz.own.resourcePath === r.path} {/each}
{#if wiz.own.creating}
{@render newResourceForm()}
{/if}
{/if} {/snippet} {#snippet newResourceForm()}
{wiz.own.form === 'string' ? 'Connection string' : 'Connection'}
{#if wiz.own.form === 'string'} wiz.own.connectionString, (v) => { wiz.own.connectionString = v absorbConnectionString(v) invalidate() } } error={!!connectionStringError} inputProps={{ placeholder: 'postgres://user:password@host:5432/database' }} /> {:else}
Host wiz.own.fields.host, (v) => setField('host', v)} inputProps={{ placeholder: 'db.example.com' }} />
Port wiz.own.fields.port ?? '', (v) => setField('port', v === '' ? undefined : Number(v)) } inputProps={{ placeholder: '5432', type: 'number' }} />
Database wiz.own.fields.dbname ?? '', (v) => setField('dbname', v)} inputProps={{ placeholder: 'postgres' }} />
SSL mode certVerification, (v) => setAdvanced('accept_invalid_certs', v === 'default' ? undefined : v === 'accept') } clearable={false} />
setAdvanced('use_iam_auth', e.detail)} options={{ right: 'Authenticate with AWS IAM' }} /> {#if wiz.own.advanced.use_iam_auth}
Region wiz.own.advanced.region, (v) => setAdvanced('region', v)} inputProps={{ placeholder: 'us-east-1' }} />
{/if}
{/snippet} {#snippet poolerWarning()} {#if poolerUnavailable}
{poolerUnavailable} Windmill connects directly instead, which needs IPv6 from the workers, or the IPv4 add-on on the project. Granting the Supabase OAuth app database_pooling_config_read and connecting again restores the pooler.
{/if} {/snippet} {#snippet reviewStep()} {#if lastFailure} {lastFailure} {/if} {#if wiz.provider === 'supabase'} {@render poolerWarning()} {:else if wiz.provider === 'instance'} {:else if !wiz.own.creating} {/if} {#if mintsResource} {/if} {#if sharesDatabaseWith} {sharesDatabaseWith.name} already uses this database. Both data tables would write to the same schema, so each one's tables are visible to the other and two tables of the same name collide. Migrations are tracked per data table, so those stay separate. {/if} {#if wiz.provider === 'supabase'} Your Supabase sign-in is not stored. If the database password ever changes, anyone with access to the project can sign in and reconnect it. Deleting the data table never deletes the Supabase project. {/if} {/snippet}