ResolveSpec/SCHEMA_TABLE_HANDLING.md
2025-11-07 10:28:14 +02:00

3.9 KiB

Schema and Table Name Handling

This document explains how the handlers properly separate and handle schema and table names.

Implementation

Both resolvespec and restheadspec handlers now properly handle schema and table name separation through the following functions:

  • parseTableName(fullTableName) - Splits "schema.table" into separate components
  • getSchemaAndTable(defaultSchema, entity, model) - Returns schema and table separately
  • getTableName(schema, entity, model) - Returns the full "schema.table" format

Priority Order

When determining the schema and table name, the following priority is used:

  1. If TableName() contains a schema (e.g., "myschema.mytable"), that schema takes precedence
  2. If model implements SchemaProvider, use that schema
  3. Otherwise, use the defaultSchema parameter from the URL/request

Scenarios

Scenario 1: Simple table name, default schema

type User struct {
    ID   string
    Name string
}

func (User) TableName() string {
    return "users"
}
  • Request URL: /api/public/users
  • Result: schema="public", table="users", fullName="public.users"

Scenario 2: Table name includes schema

type User struct {
    ID   string
    Name string
}

func (User) TableName() string {
    return "auth.users"  // Schema included!
}
  • Request URL: /api/public/users (public is ignored)
  • Result: schema="auth", table="users", fullName="auth.users"
  • Note: The schema from TableName() takes precedence over the URL schema

Scenario 3: Using SchemaProvider

type User struct {
    ID   string
    Name string
}

func (User) TableName() string {
    return "users"
}

func (User) SchemaName() string {
    return "auth"
}
  • Request URL: /api/public/users (public is ignored)
  • Result: schema="auth", table="users", fullName="auth.users"

Scenario 4: Table name includes schema AND SchemaProvider

type User struct {
    ID   string
    Name string
}

func (User) TableName() string {
    return "core.users"  // This wins!
}

func (User) SchemaName() string {
    return "auth"  // This is ignored
}
  • Request URL: /api/public/users
  • Result: schema="core", table="users", fullName="core.users"
  • Note: Schema from TableName() takes highest precedence

Scenario 5: No providers at all

type User struct {
    ID   string
    Name string
}
// No TableName() or SchemaName()
  • Request URL: /api/public/users
  • Result: schema="public", table="users", fullName="public.users"
  • Uses URL schema and entity name

Key Features

  1. Automatic detection: The code automatically detects if TableName() includes a schema by checking for "."
  2. Backward compatible: Existing code continues to work
  3. Flexible: Supports multiple ways to specify schema and table
  4. Debug logging: Logs when schema is detected in TableName() for debugging

Code Locations

Handlers

  • /pkg/resolvespec/handler.go:472-531
  • /pkg/restheadspec/handler.go:534-593

Database Adapters

  • /pkg/common/adapters/database/utils.go - Shared parseTableName() function
  • /pkg/common/adapters/database/bun.go - Bun adapter with separated schema/table
  • /pkg/common/adapters/database/gorm.go - GORM adapter with separated schema/table

Adapter Implementation

Both Bun and GORM adapters now properly separate schema and table name:

// BunSelectQuery/GormSelectQuery now have separated fields:
type BunSelectQuery struct {
    query      *bun.SelectQuery
    schema     string // Separated schema name
    tableName  string // Just the table name, without schema
    tableAlias string
}

When Model() or Table() is called:

  1. The full table name (which may include schema) is parsed
  2. Schema and table name are stored separately
  3. When building joins, the already-separated table name is used directly

This ensures consistent handling of schema-qualified table names throughout the codebase.