✏️

Inflector

String case transformation and English inflection inside SQL. snake_case ↔ camelCase ↔ PascalCase ↔ kebab-case, plus pluralize / singularize / table-name / class-name / module-name helpers. Apply to single strings, structs, or whole result sets.

Install

-- Install the extension
INSTALL inflector FROM community;

-- Load it into your session
LOAD inflector;

-- Single-value case transforms
SELECT inflector_to_camel_case('hello_world');   -- helloWorld
SELECT inflector_to_snake_case('HelloWorld');    -- hello_world
SELECT inflector_to_kebab_case('helloWorld');    -- hello-world

-- Pluralize / singularize English words
SELECT inflector_to_plural('person');            -- people
SELECT inflector_to_singular('mice');            -- mouse

-- Rewrite every key in a struct / result set
SELECT inflect('snake', {'firstName':'John','lastName':'Doe'});
SELECT * FROM inflect('kebab', (FROM read_csv('data.csv')));

Technical Overview

Why Use Inflector from DuckDB?

The string-shaping toolkit Rails and ActiveSupport made famous, available as DuckDB scalar and table functions. Convert between every common naming convention, pluralize and singularize English words, and rewrite struct keys or whole result sets in one call — without dropping into Python or a UDF.

✏️ What this extension is for

ETL and modeling work where the names matter: API responses come back in camelCase, your warehouse runs snake_case, your dashboards want Title Case, and your migration script needs user_accounts from UserAccount. Inflector handles the mechanical part in SQL.

  • Normalize column names from external sources: CSV / JSON / API ingest with mixed casing — call inflect('snake', ...) once and every column comes back consistent. No per-column rename boilerplate.
  • Reshape struct columns: inflect recurses through STRUCT keys, so a payload column with camelCase fields becomes snake_case in one pass — useful when storing API responses next to native warehouse data.
  • Generate Rails-style names: Build table names, class names, and foreign-key columns from each other: inflector_to_table_case, inflector_to_class_case, inflector_to_foreign_key. Handy when emitting DDL or migration code from SQL.
  • Inflect human-readable text: inflector_to_plural, inflector_to_singular, inflector_ordinalize, inflector_to_title_case, inflector_to_sentence_case — for labels, headings, and the kind of microcopy generated by reports.

⚙️ How it works

Inflector is a thin DuckDB wrapper over the cruet and convert_case Rust crates — the same Rails-Inflector heritage exposed as scalar functions plus one polymorphic dispatcher.

  • Two ways to pick a case: One named function per target case (camel, snake, pascal, kebab, train, screaming-snake, title, sentence, class, table) when the case is known at parse time, plus a polymorphic inflect that takes the case as a runtime argument when it isn't.
  • Polymorphic across shapes: inflect accepts a VARCHAR, a STRUCT, or a query — string in / string out, struct in / struct with renamed keys out, query in / query with renamed columns out. Same function name across all three shapes.
  • Predicates for routing: A matching is_* predicate for each case, useful when normalizing a column whose values arrive in mixed conventions.
  • Rails / ActiveSupport-style helpers: Foreign-key conversion (Messagemessage_id), demodulize (ActiveRecord::CoreExtensions::StringString), deconstantize (Net::HTTPNet), ordinalize (11st), and the inverse — the familiar Inflector toolkit, ported to SQL.

🛡️ Caveats

What to know before relying on inflection in a production pipeline.

  • Pluralization is English-only: Singular/plural rules wrap the cruet Rust crate, which ships English rules and irregular forms (person/people, mouse/mice). Non-English text returns wrong output silently — keep a real linguistic library upstream for multilingual data.
  • Case transforms are mechanical, not semantic: inflector_to_snake_case('XMLHttpRequest') yields xml_http_request — the underlying tokenizer treats acronyms as runs of capitals. That's right for column-naming work; if you need acronym preservation rules tuned to your domain, do that step in application code first.
  • Pluralization isn't always reversible: peopleperson is fine, but English has true ambiguities (data is both singular and plural in modern usage; species is unchanged). Don't round-trip a value through plural-then-singular and assume identity.
  • Single-character tokens are skipped: The case tokenizer ignores 1-character runs; this matters for inputs like aB or single-letter prefixes. Two-character minimum on tokens.

🎯 Common Use Cases

Snake-case an entire CSV / JSON ingest

Wrap read_csv / read_json in inflect('snake', ...) to rewrite every column header in one call. The fastest path from a firstName,lastName,emailAddress CSV to a clean warehouse table.

Reshape struct payloads

API responses stored as STRUCT columns — inflect('snake', payload) returns the same struct with every key renamed, recursively, leaving values untouched.

Generate Rails-style DDL from a class list

From a column of class names, build table names with inflector_to_table_case and foreign keys with inflector_to_foreign_key — useful when emitting migration SQL from a model registry.

Format report labels

Pluralize counts, ordinalize positions, title-case headings — the kind of microcopy that otherwise pulls SQL out into application code.

Deep Dive

Technical Details

The one-call rename

The single most useful pattern: take a CSV or JSON file with camelCase headers and snake-case the entire result set in one statement, no per-column boilerplate:

SELECT *
FROM inflect('snake', (FROM read_csv('https://example.com/users.csv')));
-- firstName,lastName,emailAddress  →  first_name,last_name,email_address

inflect is polymorphic on its second argument — pass it a VARCHAR, a STRUCT, or a parenthesized query. Same function name, three shapes:

-- 1. A single string
SELECT inflect('snake', 'helloWorld');                          -- 'hello_world'

-- 2. A struct — every key renamed, values untouched
SELECT inflect('snake', {'firstName':'John','lastName':'Doe'});
-- {'first_name': 'John', 'last_name': 'Doe'}

-- 3. A query result — every column header renamed
SELECT * FROM inflect('kebab', (FROM read_json('events.json')));

That’s the headline feature. The rest of the surface is Rails / ActiveSupport-style scalar helpers for individual values.

Pluralization is English-only

inflector_to_plural and inflector_to_singular wrap the cruet Rust crate, whose ruleset and irregular-word table are English. They handle real English irregulars well — personpeople, mousemice, analysisanalyses, quizquizzes — but they have no awareness of French, German, Spanish, or any other language. Pass non-English input and you’ll get a mechanical English-rule output, silently. For multilingual content, do inflection upstream and store the result.

A handful of edge cases inside English are also worth knowing:

  • Modern uncountablesdata, species, series — round-trip irregularly.
  • Mechanical case tokenizationinflector_to_snake_case('XMLHttpRequest') yields xml_http_request. Right for column work; tune in app code if you need different acronym rules.
  • Single-character tokens are skipped. The tokenizer requires a 2-character minimum, so aBb-prefixed tokens are dropped.

Case formats supported

Every shape Rails / ActiveSupport ships, plus the obvious extras:

FormatExampleTo-function
camelCasehelloWorldinflector_to_camel_case
PascalCase / ClassCaseHelloWorldinflector_to_pascal_case / inflector_to_class_case
snake_casehello_worldinflector_to_snake_case
kebab-casehello-worldinflector_to_kebab_case
Train-CaseHello-Worldinflector_to_train_case
SCREAMING_SNAKE_CASEHELLO_WORLDinflector_to_screamingsnake_case
Title CaseHello Worldinflector_to_title_case
Sentence caseHello worldinflector_to_sentence_case
table_case (snake plural)user_accountsinflector_to_table_case

Each has a matching inflector_is_* predicate — useful when you need to route a value based on its current shape rather than blindly transform.

Inflection helpers

Beyond pure case work:

These are direct ports of the corresponding ActiveSupport::Inflector methods.

Inflector vs RapidFuzz

Both extensions live in the Matching & casing group, but they answer different questions:

  • Use Inflector when you know exactly what shape you want (snake_case, kebab-case, plural, foreign-key) and need to mechanically produce it. Single-input, deterministic output.
  • Use rapidfuzz when you have two strings and want a similarity score — record linkage, dedupe, spell-check. Fuzzy comparison, not transformation.
  • Both can coexist — normalize naming with Inflector, then fuzzy-match across normalized values with RapidFuzz.

Source

Inflector is implemented in C++ with a Rust binding (cruet + convert_case) for the actual rule engine. Source at github.com/Query-farm/inflector.

Reference

Extension Contents

Quick reference to all available functions and settings organized by category.

Name Description
Bulk

Apply a transform to every key in a struct or every column name in a query result, in a single call. The headline feature for normalizing API responses or migrating between naming conventions.

inflect() Apply a named transform ('camel', 'snake', 'pascal', 'kebab', etc
Case Transformation

Convert strings between naming conventions: snake_case, camelCase, PascalCase, kebab-case, Train-Case, SCREAMING_SNAKE_CASE, Title Case, Sentence case. Also raw upper/lower transforms.

inflector_to_camel_case() Convert to camelCase
inflector_to_class_case() Convert to ClassCase (PascalCase singular)
inflector_to_kebab_case() Convert to kebab-case (hyphen-separated)
inflector_to_lower_case() Lowercase every character
inflector_to_pascal_case() Convert to PascalCase
inflector_to_screamingsnake_case() Convert to SCREAMING_SNAKE_CASE (caps with underscores)
inflector_to_sentence_case() Convert to Sentence case — first letter capital, rest lowercase, words space-separated
inflector_to_snake_case() Convert to snake_case
inflector_to_title_case() Convert to Title Case — every word capitalized, space-separated
inflector_to_train_case() Convert to Train-Case (capitalized hyphenated)
inflector_to_upper_case() Uppercase every character
Configuration
inflector_acronyms List of acronyms preserved as uppercase in case conversions (e
Inflection

English plural / singular conversion. Handles irregulars (personpeople, mousemice).

inflector_to_plural() Pluralize an English word — handles irregulars (person→people, mouse→mice)
inflector_to_singular() Singularize an English word
Naming Helpers

Convenience builders for Rails-style naming — table_case, foreign-key generation, ClassCase, and module-path conversion.

inflector_to_table_case() Rails-style 'table_case' — snake_case + plural

API Reference

Function Documentation

Detailed documentation for each function including signatures, parameters, and examples.

inflect

Scalar Function Bulk
Signature
inflect(format: VARCHAR, text: VARCHAR) → VARCHAR
Parameters (Positional)
Parameter Type Mode Description
format VARCHAR Positional
text VARCHAR Positional
Returns
Description

Apply a named transform ('camel', 'snake', 'pascal', 'kebab', etc.) to one of: a string, every key in a struct, or every column name in a query result. The headline function for renaming a whole shape in one call.

Examples
1

Rewrite struct keys

SELECT inflect('snake', {'firstName':'John','lastName':'Doe'});
2

Rewrite every column of a query

SELECT * FROM inflect('kebab', (FROM read_csv('data.csv')));

inflector_deconstantize

Scalar Function
Signature
inflector_deconstantize(constant: VARCHAR) → VARCHAR
Parameters (Positional)
Parameter Type Mode Description
constant VARCHAR Positional
Returns
Description

Removes the rightmost segment from a constant expression

Examples
1
SELECT inflector_deconstantize('Net::HTTP');

Output

inflector_deconstantize('Net::HTTP')
Net

inflector_demodulize

Scalar Function
Signature
inflector_demodulize(qualified_name: VARCHAR) → VARCHAR
Parameters (Positional)
Parameter Type Mode Description
qualified_name VARCHAR Positional
Returns
Description

Removes the module part from a fully qualified name

Examples
1
SELECT inflector_demodulize('ActiveRecord::CoreExtensions::String');

Output

inflector_demodulize('ActiveRecord::CoreExtensions::String')
String

inflector_deordinalize

Scalar Function
Signature
inflector_deordinalize(ordinal: VARCHAR) → VARCHAR
Parameters (Positional)
Parameter Type Mode Description
ordinal VARCHAR Positional
Returns
Description

Removes the ordinal suffix from a string (1st -> 1)

Examples
1
SELECT inflector_deordinalize('1st');

Output

inflector_deordinalize('1st')
1

inflector_is_camel_case

Scalar Function
Signature
inflector_is_camel_case(text: VARCHAR) → BOOLEAN
Parameters (Positional)
Parameter Type Mode Description
text VARCHAR Positional
Returns
Description

Returns true if the string is in camelCase format

Examples
1
SELECT inflector_is_camel_case('helloWorld');

Output

inflector_is_camel_case('helloWorld')
true

inflector_is_class_case

Scalar Function
Signature
inflector_is_class_case(text: VARCHAR) → BOOLEAN
Parameters (Positional)
Parameter Type Mode Description
text VARCHAR Positional
Returns
Description

Returns true if the string is in ClassCase (PascalCase) format

Examples
1
SELECT inflector_is_class_case('HelloWorld');

Output

inflector_is_class_case('HelloWorld')
true

inflector_is_foreign_key

Scalar Function
Signature
inflector_is_foreign_key(text: VARCHAR) → BOOLEAN
Parameters (Positional)
Parameter Type Mode Description
text VARCHAR Positional
Returns
Description

Returns true if the string is in foreign key format (ends with _id)

Examples
1
SELECT inflector_is_foreign_key('message_id');

Output

inflector_is_foreign_key('message_id')
true

inflector_is_kebab_case

Scalar Function
Signature
inflector_is_kebab_case(text: VARCHAR) → BOOLEAN
Parameters (Positional)
Parameter Type Mode Description
text VARCHAR Positional
Returns
Description

Returns true if the string is in kebab-case format

Examples
1
SELECT inflector_is_kebab_case('hello-world');

Output

inflector_is_kebab_case('hello-world')
true

inflector_is_pascal_case

Scalar Function
Signature
inflector_is_pascal_case(text: VARCHAR) → BOOLEAN
Parameters (Positional)
Parameter Type Mode Description
text VARCHAR Positional
Returns
Description

Returns true if the string is in PascalCase format

Examples
1
SELECT inflector_is_pascal_case('HelloWorld');

Output

inflector_is_pascal_case('HelloWorld')
true

inflector_is_screamingsnake_case

Scalar Function
Signature
inflector_is_screamingsnake_case(text: VARCHAR) → BOOLEAN
Parameters (Positional)
Parameter Type Mode Description
text VARCHAR Positional
Returns
Description

Returns true if the string is in SCREAMING_SNAKE_CASE format

Examples
1
SELECT inflector_is_screamingsnake_case('HELLO_WORLD');

Output

inflector_is_screamingsnake_case('HELLO_WORLD')
true

inflector_is_sentence_case

Returns true if the string is in Sentence case format

inflector_is_snake_case

Returns true if the string is in snake_case format

inflector_is_table_case

Returns true if the string is in table_case format (snake_case plural)

inflector_is_title_case

Returns true if the string is in Title Case format

inflector_is_train_case

Returns true if the string is in Train-Case format

inflector_ordinalize

Converts a number string to its ordinal form (1st, 2nd, 3rd, etc.

inflector_to_camel_case

Case Transformation

Convert to camelCase.

inflector_to_class_case

Case Transformation

Convert to ClassCase (PascalCase singular).

inflector_to_foreign_key

Converts a class name to a foreign key column name

inflector_to_kebab_case

Case Transformation

Convert to kebab-case (hyphen-separated).

inflector_to_lower_case

Case Transformation

Lowercase every character.

inflector_to_pascal_case

Case Transformation

Convert to PascalCase.

inflector_to_plural

Inflection

Pluralize an English word — handles irregulars (person→people, mouse→mice).

inflector_to_screamingsnake_case

Case Transformation

Convert to SCREAMING_SNAKE_CASE (caps with underscores).

inflector_to_sentence_case

Case Transformation

Convert to Sentence case — first letter capital, rest lowercase, words space-separated.

inflector_to_singular

Inflection

Singularize an English word.

inflector_to_snake_case

Case Transformation

Convert to snake_case.

inflector_to_table_case

Naming Helpers

Rails-style 'table_case' — snake_case + plural.

inflector_to_title_case

Case Transformation

Convert to Title Case — every word capitalized, space-separated.

inflector_to_train_case

Case Transformation

Convert to Train-Case (capitalized hyphenated).

inflector_to_upper_case

Case Transformation

Uppercase every character.

Configuration

Settings

Configure the inflector extension behavior using these pragma settings.

inflector_acronyms

List of acronyms preserved as uppercase in case conversions (e.g., HTML, API)

Default Value: '[]'

Practical Examples

Cookbook

Real-world recipes and patterns for common use cases.

Quick reference

NeedPick
Convert one string between casesinflector_to_<case> or inflect(case_name, value)
Rewrite every key in a structinflect('snake', struct_col)
Rewrite every column of a querySELECT * FROM inflect('snake', (FROM ...))
Pluralize / singularize an English wordinflector_to_plural / inflector_to_singular
Rails-style table name from class nameinflector_to_table_case
Class name → foreign-key columninflector_to_foreign_key
Detect what case a string is ininflector_is_* predicates
Number → ordinal labelinflector_ordinalize

Single-string transforms

SELECT inflector_to_camel_case('hello_world');                 -- 'helloWorld'
SELECT inflector_to_snake_case('HelloWorld');                  -- 'hello_world'
SELECT inflector_to_pascal_case('hello_world');                -- 'HelloWorld'
SELECT inflector_to_kebab_case('helloWorld');                  -- 'hello-world'
SELECT inflector_to_train_case('helloWorld');                  -- 'Hello-World'
SELECT inflector_to_screamingsnake_case('helloWorld');         -- 'HELLO_WORLD'
SELECT inflector_to_title_case('hello_world');                 -- 'Hello World'
SELECT inflector_to_sentence_case('helloWorld');               -- 'Hello world'

inflect(case_name, value) is the dispatching equivalent — same result, lets you parameterize the case at runtime:

SELECT inflect('camel', 'hello_world');                        -- 'helloWorld'
SELECT inflect(target_case, column_value)                      -- target_case from a config table
FROM ...;

Pluralize / singularize (English only)

SELECT inflector_to_plural('person');                          -- 'people'
SELECT inflector_to_plural('child');                           -- 'children'
SELECT inflector_to_plural('quiz');                            -- 'quizzes'

SELECT inflector_to_singular('mice');                          -- 'mouse'
SELECT inflector_to_singular('analyses');                      -- 'analysis'

These wrap the cruet Rust crate’s English ruleset. Non-English input gets English-rule output, silently — handle multilingual text upstream.

Rewrite struct keys

SELECT inflect('snake', {'firstName':'John','lastName':'Doe'});
-- {'first_name': 'John', 'last_name': 'Doe'}

SELECT inflect('camel', {'first_name':'John','last_name':'Doe'});
-- {'firstName': 'John', 'lastName': 'Doe'}

Apply against a struct column to normalize a stored API payload in place:

SELECT id, inflect('snake', payload) AS payload_snake
FROM api_events;

Rewrite every column of a query

The killer ETL pattern — normalize the headers of a remote file in one statement:

-- camelCase CSV → snake_case columns
SELECT * FROM inflect('snake', (FROM read_csv('https://example.com/users.csv')));

-- Mixed-casing JSON → kebab-case columns
SELECT * FROM inflect('kebab', (FROM read_json('events.json')));

Wrap with CREATE TABLE AS to land it in the warehouse with clean names:

CREATE TABLE users AS
SELECT * FROM inflect('snake', (FROM read_csv('users.csv')));

Build Rails-style derivations

The classic class ↔ table ↔ foreign-key triangle:

SELECT inflector_to_table_case('UserAccount');                 -- 'user_accounts' (table)
SELECT inflector_to_class_case('user_accounts');               -- 'UserAccount'   (class)
SELECT inflector_to_foreign_key('Message');                    -- 'message_id'    (FK column)

Useful when emitting DDL or migration code from a model registry:

SELECT
  class_name,
  inflector_to_table_case(class_name)   AS table_name,
  inflector_to_foreign_key(class_name)  AS fk_column
FROM models;

Detect-then-transform

Use inflector_is_* predicates to route values whose case you don’t trust on the way in:

SELECT
  raw,
  CASE
    WHEN inflector_is_snake_case(raw)         THEN raw
    WHEN inflector_is_camel_case(raw)         THEN inflector_to_snake_case(raw)
    WHEN inflector_is_pascal_case(raw)        THEN inflector_to_snake_case(raw)
    WHEN inflector_is_kebab_case(raw)         THEN replace(raw, '-', '_')
    ELSE inflector_to_snake_case(raw)
  END AS normalized
FROM mixed_input;

Ordinalize numbers

SELECT inflector_ordinalize('1');                              -- '1st'
SELECT inflector_ordinalize('22');                             -- '22nd'
SELECT inflector_ordinalize('103');                            -- '103rd'

SELECT inflector_deordinalize('1st');                          -- '1'

Useful for report labels: 'Top ' || inflector_ordinalize(rank::VARCHAR) || ' result'.

Strip module / namespace prefixes

SELECT inflector_demodulize('ActiveRecord::CoreExtensions::String');  -- 'String'
SELECT inflector_deconstantize('Net::HTTP');                          -- 'Net'

For working with stack traces or class names imported from systems that use Ruby- or C++-style namespaces.

Pair with RapidFuzz for normalize-then-match

If you’re deduping records that arrive in mixed cases, normalize first, then fuzzy-match — the rapidfuzz extension is the sibling tool:

SELECT a.id, b.id,
       rapidfuzz_ratio(
         inflector_to_snake_case(a.name),
         inflector_to_snake_case(b.name)
       ) AS sim
FROM left_records a, right_records b
WHERE rapidfuzz_ratio(
        inflector_to_snake_case(a.name),
        inflector_to_snake_case(b.name)
      ) > 90;

This is why both extensions share the Matching & casing group — they compose.

Platform Support

Compatibility

Extension availability may vary by platform and DuckDB version. Check below to ensure this extension supports your environment before installation.

Quick Facts

Software License MIT
Pricing Free
Written In Rust
Source Available Yes
View on GitHub
Usage
17,947+
loads in last 30 days

Platforms

Linux
Linux (musl)
macOS
Windows
WASM
Supported platform architectures
Linux: x86_64, aarch64
Linux (musl): x86_64
macOS: Apple Silicon, Intel
Windows: x86_64
WASM: mvp, threads, eh
Compiled binary sizes
Platform Architecture Size
Linux aarch64 4.41 MB
Linux x86_64 4.84 MB
Linux (musl) x86_64 4.59 MB
macOS Apple Silicon 2.66 MB
macOS Intel 3.01 MB
Windows x86_64 8.15 MB
WASM eh 357.9 KB
WASM mvp 355.9 KB
WASM threads 356.0 KB

Gzipped download size from the DuckDB community-extensions registry.

DuckDB Versions

Release calendar
Supported
v1.4.4 v1.5.2

Case & Inflection in SQL

Install Inflector to convert between naming conventions (snake/camel/pascal/kebab/train/title), pluralize words, and rewrite struct keys or whole result sets in one shot.