Skip to content

feat(postgresql): support custom metadata schema via dinky.metadata.s… - #4585

Open
huge86 wants to merge 1 commit into
DataLinkDC:devfrom
huge86:feat/postgresql-custom-schema
Open

huge86 wants to merge 1 commit into
DataLinkDC:devfrom
huge86:feat/postgresql-custom-schema

Conversation

@huge86

@huge86 huge86 commented Oct 13, 2026

Copy link
Copy Markdown

Feature Overview
Allow Dinky to use a custom PostgreSQL schema (e.g., dinky) instead of being forced to use the default public schema. A single configuration entry dinky.metadata.schema controls JDBC URL, Flyway migrations, and MyBatis-Plus SQL generation — all three layers stay in sync.
• Default: public (fully backward-compatible, existing deployments need zero changes)
• Config override: dinky.metadata.schema: dinky in application-postgresql.yml
• Env override: export DINKY_METADATA_SCHEMA=dinky

Background

When deploying Dinky with PostgreSQL as metadata DB, moving tables from the default public schema to a custom schema (e.g., dinky) causes two failures:

  1. Flyway creates tables in public — all migration scripts hardcode public. prefix
  2. MyBatis-Plus SQL queries don't prepend schema — SELECT * FROM dinky_task instead of SELECT * FROM dinky.dinky_task

Solution Architecture

┌──────────────────────────────────────────────────────────────────┐
│ application-postgresql.yml │
│ │
│ dinky.metadata.schema = ${DINKY_METADATA_SCHEMA:public} │
│ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ JDBC URL Flyway MyBatis-Plus │
│ currentSchema=... default-schema=... SchemaInterceptor │
│ schemas=... placeholders=... (@Profile("pg")) │
│ ${dinky_schema}.dinky_task │
│ in migration scripts │
└──────────────────────────────────────────────────────────────────┘

Changes (15 files)

New Files (2)

File
Purpose
DinkyMetadataProperties.java
@ConfigurationProperties(prefix = "dinky.metadata"), default schema = "public"
DinkySchemaInnerInterceptor.java
Extends DynamicTableNameInnerInterceptor, prefix-matches all dinky_* tables → schema.table_name

Modified Files (13)

Configuration:
File
Change
MybatisPlusConfig.java
Enable DinkyMetadataProperties via @EnableConfigurationProperties; register DinkySchemaInnerInterceptor as @Profile("postgresql") Bean; use ObjectProvider.ifAvailable() for optional registration so non-PostgreSQL profiles are unaffected
application-postgresql.yml
Add dinky.metadata.schema config key; JDBC URL auto-append currentSchema=${dinky.metadata.schema:public}; override Flyway with default-schema, schemas, create-schemas, and placeholders.dinky_schema
Flyway Migration Scripts (10 files):
File
Pattern
V20240506.1.0.2__baseline.sql
public. → ${dinky_schema}.; 'public'→ '${dinky_schema}'
V20240708.1.1.0__release.sql
Same
V20240729.1.1.0__release.sql
Same
V20240827.1.2.0__release.sql
Same
V20241203.1.2.0__release.sql
Same
V20241205.1.2.0__release.sql
Same + enabled = 0 → enabled::int = 0(5×, compatibility for historical SMALLINT→BOOLEAN migration backup-tables)
V20241217.1.2.0__release.sql
Same
V20250301.1.3.0__release.sql
Same
R20240708.1.1.0__release.sql
Same
R20240729.1.1.0__release.sql
Same
Manual SQL Script:
File
Change
script/sql/dinky-pg.sql
Remove all public. prefixes → depends on currentSchema connection param or SET search_path

Design Decisions & Verification

Decision
Rationale
Verification
Prefix-based match (dinky_*) instead of explicit table-name map
Future Dinky tables auto-covered; no list maintenance needed
Code review confirms no missed tables in baseline
@Profile("postgresql") Bean
Schema interceptor only active under PostgreSQL
MySQL/H2 profiles unaffected; ObjectProvider.ifAvailable() ensures graceful no-op
Interceptor order: TenantLine → DinkySchema → Pagination
Critical: tenant's ignoreTable() must see original (un-prefixed) table names to match correctly
Comments in code; ordering validated
Flyway placeholder override in application-postgresql.yml
Keeps application-flyway.yml as generic default (placeholder-prefix: '##{', placeholder-replacement: false) so MySQL isn't affected
application-flyway.yml unchanged; Spring Boot profile override mechanism verified
MyBatis-Plus 3.5.7 API: setTableNameHandler(TableNameHandler)
Decompiled the actual 3.5.7 jar — setTableNameHandlerMapdoes NOT exist in 3.5.7 (common pitfall from reading newer master docs)
Jar decompilation confirmed; javap -pshows only setTableNameHandler(TableNameHandler)API
validate-on-migrate: false (pre-existing)
Modified migration scripts won't break already-deployed environments
Changes only affect fresh installs
Manual SQL script uses non-qualified names
script/sql/dinky-pg.sql is a standalone manual-init script (no Flyway placeholders); relies on currentSchemaconnection param
Correct approach for non-Flyway SQL

Non-Goals (Explicitly Out of Scope)

• No MySQL/H2 changes — schema interceptor is PostgreSQL-profile gated
• No application-flyway.yml changes — kept as generic default; PostgreSQL-specific overrides go in application-postgresql.yml
• No boolean/integer fix — that's in PR1 (separate PR; recommended to merge PR1 first)

Backward Compatibility

• ✅ Default public schema: interceptor short-circuits when schema = "public" (no-op handler); zero behavioral change for existing users
• ✅ MySQL/H2 unaffected: schema interceptor Bean only registered under @Profile("postgresql")
• ✅ Flyway checksum: validate-on-migrate: false means modified migration scripts won't trigger checksum failures on deployed environments

Usage

Option A — Config file

application-postgresql.yml

dinky:
metadata:
schema: dinky

Option B — Environment variable (recommended for Docker/K8s)

export DINKY_METADATA_SCHEMA=dinky

Database-side preparation

CREATE SCHEMA dinky AUTHORIZATION dinky;
GRANT ALL PRIVILEGES ON SCHEMA dinky TO dinky;

Continue using public (no changes needed)

Simply don't configure dinky.metadata.schema. The default value public applies everywhere.

Verification Checklist

MyBatis-Plus 3.5.7 jar decompiled — setTableNameHandler exists, setTableNameHandlerMap does not
DinkySchemaInnerInterceptor.java manual javac compilation — PASS (exit code 0)
TenantLine interceptor ordering preserved — ignoreTable uses original un-prefixed table names
Flyway placeholder-prefix/suffix matches ${dinky_schema} usage in scripts (placeholder-prefix: '${', placeholder-suffix: '}')
application-flyway.yml kept as generic default (placeholder-prefix: '##{') — not modified
All 10 PostgreSQL migration scripts updated consistently (same pattern across all)
V20241205.1.2.0__release.sql correctly handles historical SMALLINT→BOOLEAN migration backup-tables with enabled::int = 0
Default schema public → interceptor returns immediately (no-op), zero behavioral change

Prerequisite

This PR does not fix the operator does not exist: boolean = integer error on startup. Please merge PR1: fix(postgresql): resolve boolean = integer operator error first (or both in any order — they're independent).

Change Summary

15 files changed, 1528 insertions(+), 1395 deletions(-)

New:
DinkyMetadataProperties.java (+40 lines)
DinkySchemaInnerInterceptor.java (+60 lines)

Modified:
MybatisPlusConfig.java (profile-gated interceptor wiring)
application-postgresql.yml (schema config + Flyway override)
10 PostgreSQL Flyway migration scripts (public. → ${dinky_schema}.)
script/sql/dinky-pg.sql (remove public. prefixes)

…chema

Allow Dinky metadata tables to be created in a custom PostgreSQL schema
(e.g. 'dinky' instead of the default 'public'). A single configuration
entry controls JDBC URL, Flyway migrations, and MyBatis-Plus table
name resolution.

Configuration:
  dinky.metadata.schema: dinky  (default: public)
  or environment variable DINKY_METADATA_SCHEMA

Changes:
  New files:
  - DinkyMetadataProperties.java: schema config properties class
  - DinkySchemaInnerInterceptor.java: MyBatis-Plus DynamicTableNameInnerInterceptor
    that dynamically prepends schema prefix to all dinky_* table names
    (uses official setTableNameHandler(TableNameHandler) API with prefix-based
    matching, so any future dinky_* tables are automatically covered)

  Modified files:
  - application-postgresql.yml: added dinky.metadata.schema config,
    JDBC URL currentSchema parameter, Flyway default-schema/placeholders
  - MybatisPlusConfig.java: register schema interceptor under postgresql
    profile (between TenantLine and Pagination interceptors, using
    ObjectProvider so non-postgresql profiles are unaffected)
  - 10 Flyway migration scripts: replaced hardcoded 'public.' with Flyway
    placeholder '\.' and string literals 'public' with
    '\'
  - V20241205 migration script: 'enabled = 0' -> 'enabled::int = 0' for
    PostgreSQL boolean column compatibility during historical SMALLINT
    to BOOLEAN data migration
  - script/sql/dinky-pg.sql: removed all 'public.' prefixes so the
    manual initialization script works with any search_path

Design notes:
  - Default schema is 'public', fully backward compatible
  - MyBatis-Plus schema interceptor is Profile-gated to postgresql only
  - Interceptor order: TenantLine -> DinkySchema -> Pagination, ensuring
    tenant ignoreTable matches original table names
  - Flyway validate-on-migrate remains false, so checksum changes for
    already-executed scripts do not break existing deployments
  - All other database profiles (MySQL, etc.) are unaffected

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant