Skip to content

PostGraphile v5 Cannot Handle Database Column Names with Non-ASCII Characters #2837

@zuohuadong

Description

@zuohuadong

Issue Description

PostGraphile v5 fails to generate GraphQL schema when database tables/views contain column names with non-ASCII characters (e.g., Chinese, Japanese, Korean characters), resulting in schema generation failure.

Error Message

TypeError: Object type 'TJcBmnfV' attempted to define a field with invalid name "期间". 
Names must only contain [_a-zA-Z0-9] but "期间" does not.
at assertValidName (/node_modules/graphile-build/dist/makeNewBuild.js:373:27)
at fieldWithHooks (/node_modules/graphile-build/dist/newWithHooks/index.js:104:57)
at processAttribute (/node_modules/graphile-build-pg/dist/plugins/PgAttributesPlugin.js:111:39)

Environment

  • PostGraphile Version: v5 (latest)
  • Runtime: Bun v1.3.3
  • Database: PostgreSQL
  • OS: macOS

Steps to Reproduce

  1. Create a database view with non-ASCII column names:
CREATE VIEW t_jc_bmnf_v AS
SELECT 
    id,
    "期间" as period,  -- Chinese column name
    name
FROM some_table;
  1. Configure PostGraphile v5:
const preset: GraphileConfig.Preset = {
  extends: [PostGraphileAmberPreset, PgSimplifyInflectionPreset],
  pgServices: [
    makePgService({
      connectionString: DATABASE_URL,
      schemas: ['public'],
    }),
  ],
}
  1. Start PostGraphile - schema generation fails

Expected Behavior

PostGraphile should be able to:

  1. Automatically skip fields with non-ASCII column names
  2. Transform non-ASCII column names into valid GraphQL field names
  3. Provide configuration/plugin options for users to customize handling

Actual Behavior

PostGraphile calls fieldWithHooks directly in PgAttributesPlugin.processAttribute, which invokes assertValidName to validate field names. This validation occurs before any user-defined hooks can intercept, making it impossible to handle via plugins.

Attempted Solutions

1. Using @omit Smart Tags (Failed)

COMMENT ON VIEW t_jc_bmnf_v IS '@omit';

Result: v5 doesn't seem to support this or requires additional configuration

2. Using entityBehavior to Hide Resources (Failed)

schema: {
  entityBehavior: {
    pgResource: {
      inferred(behavior: string, resource: any) {
        if (SKIP_TABLES.includes(resource.name)) {
          return `${behavior} -*`
        }
        return behavior
      },
    },
  },
}

Result: Only hides query fields, but types are still created and field name validation still fails

3. Using pgCodecAttribute Behavior (Failed)

pgCodecAttribute: {
  inferred(behavior: string, entity: any) {
    const attributeName = entity.attribute.name
    if (/[^\x00-\x7F]/.test(attributeName)) {
      return `${behavior} -*`
    }
    return behavior
  },
}

Result: Field name validation occurs before behavior checks

4. Using Schema Hooks (Failed)

hooks: {
  GraphQLObjectType_fields(fields, build, context) {
    // Attempt to return empty fields or filter fields
  },
}

Result: Hook executes after PgAttributesPlugin, validation has already failed

5. Using Inflection Override (Failed)

inflection: {
  replace: {
    attribute(previous, options, details) {
      // Attempt to rename non-ASCII fields
    },
  },
}

Result: Inflection executes after field name validation

6. Using Gather Hooks (Failed)

gather: {
  hooks: {
    pgCodecs_PgCodec(info, event) {
      // Attempt to modify or skip codec
    },
  },
}

Result: Cannot prevent type creation

Root Cause

In PostGraphile v5's architecture, field name validation occurs in the processAttribute function of PgAttributesPlugin:

// PgAttributesPlugin.js:111
const fieldName = inflection.attribute({ codec, attributeName, ... });
const field = fieldWithHooks({  // <- This calls assertValidName
  fieldName,
  ...
});

assertValidName is a mandatory requirement of the GraphQL specification that disallows non-ASCII characters. This validation occurs before any user-interceptable hooks.

Proposed Solutions

Solution A: Filter at Gather Stage (Recommended)

Filter out attributes with non-ASCII characters before PgAttributesPlugin processes them:

// In PgAttributesPlugin, before processAttribute
if (/[^\x00-\x7F]/.test(attributeName)) {
  return; // Skip this attribute
}

Solution B: Provide Configuration Options

Allow users to configure how to handle non-ASCII field names:

gather: {
  pgNonAsciiFieldBehavior: 'skip' | 'sanitize' | 'error',
  pgFieldNameSanitizer: (name: string) => string,
}

Solution C: Support Smart Tags

Restore support for @omit and other smart tags in v5, or provide clear documentation on how to achieve the same functionality in v5.

Current Workaround

#!/usr/bin/env bun


import { readFileSync, writeFileSync } from 'fs'
import { join } from 'path'

const PLUGIN_PATH = join(
  process.cwd(),
  'node_modules/graphile-build-pg/dist/plugins/PgAttributesPlugin.js'
)

const PATCH_MARKER = '// PATCHED: Skip non-ASCII attributes'

async function applyPatch() {
  console.log('📦 Applying PostGraphile patch for non-ASCII field names...')

  try {

    let content = readFileSync(PLUGIN_PATH, 'utf-8')


    if (content.includes(PATCH_MARKER)) {
      console.log('✅ Patch already applied!')
      return
    }

    // 找到 processAttribute 函数的开始
    const searchPattern = 'function processAttribute(fields, build, context, attributeName, overrideName, isNotNull) {'
    const insertAfterPattern = 'const { extend, inflection, dataplanPg: { pgSelectFromRecords, pgSelectSingleFromRecord }, } = build;'

    if (!content.includes(searchPattern)) {
      console.error('❌ Could not find processAttribute function!')
      process.exit(1)
    }


    const patchCode = `
    ${PATCH_MARKER}
    // Skip attributes with non-ASCII characters in their names
    if (/[^\\x00-\\x7F]/.test(attributeName)) {
        return;
    }
    `

   
    content = content.replace(
      insertAfterPattern,
      insertAfterPattern + patchCode
    )


    writeFileSync(PLUGIN_PATH, content, 'utf-8')

    console.log('✅ Patch applied successfully!')
    console.log('📝 Modified file:', PLUGIN_PATH)
    console.log('')
    console.log('⚠️  Note: This patch will be lost when you reinstall node_modules.')
    console.log('   Run this script again after reinstalling dependencies.')
  } catch (error) {
    console.error('❌ Error applying patch:', error)
    process.exit(1)
  }
}

applyPatch()

Impact

This issue affects all databases using non-English field names, including:

  • Chinese
  • Japanese
  • Korean
  • Arabic
  • Russian
  • Other non-ASCII character sets

Related Links

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    Status

    🌳 Triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions