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:
inflectrecurses throughSTRUCTkeys, so apayloadcolumn 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
inflectthat takes the case as a runtime argument when it isn't. - • Polymorphic across shapes:
inflectaccepts aVARCHAR, aSTRUCT, 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 (
Message→message_id), demodulize (ActiveRecord::CoreExtensions::String→String), deconstantize (Net::HTTP→Net), ordinalize (1→1st), 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
cruetRust 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')yieldsxml_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:
people→personis fine, but English has true ambiguities (datais both singular and plural in modern usage;speciesis 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
aBor 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.
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 — person ↔ people, mouse ↔ mice, analysis ↔ analyses, quiz ↔ quizzes — 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 uncountables —
data,species,series— round-trip irregularly. - Mechanical case tokenization —
inflector_to_snake_case('XMLHttpRequest')yieldsxml_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
aB⇒b-prefixed tokens are dropped.
Case formats supported
Every shape Rails / ActiveSupport ships, plus the obvious extras:
| Format | Example | To-function |
|---|---|---|
camelCase | helloWorld | inflector_to_camel_case |
PascalCase / ClassCase | HelloWorld | inflector_to_pascal_case / inflector_to_class_case |
snake_case | hello_world | inflector_to_snake_case |
kebab-case | hello-world | inflector_to_kebab_case |
Train-Case | Hello-World | inflector_to_train_case |
SCREAMING_SNAKE_CASE | HELLO_WORLD | inflector_to_screamingsnake_case |
Title Case | Hello World | inflector_to_title_case |
Sentence case | Hello world | inflector_to_sentence_case |
table_case (snake plural) | user_accounts | inflector_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:
- Pluralize / singularize —
inflector_to_plural,inflector_to_singular. - Ordinalize —
inflector_ordinalize(1→1st),inflector_deordinalize(1st→1). - Foreign keys —
inflector_to_foreign_key(Message→message_id). - Module paths —
inflector_demodulize(ActiveRecord::CoreExtensions::String→String),inflector_deconstantize(Net::HTTP→Net).
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
rapidfuzzwhen 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 ( | ||
| 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
Signature
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
Rewrite struct keys
SELECT inflect('snake', {'firstName':'John','lastName':'Doe'}); Rewrite every column of a query
SELECT * FROM inflect('kebab', (FROM read_csv('data.csv'))); Related Functions
inflector_deconstantize
Signature
Parameters (Positional)
| Parameter | Type | Mode | Description |
|---|---|---|---|
constant | VARCHAR | Positional |
Returns
Description
Removes the rightmost segment from a constant expression
Examples
SELECT inflector_deconstantize('Net::HTTP'); Output
| inflector_deconstantize('Net::HTTP') |
|---|
| Net |
inflector_demodulize
Signature
Parameters (Positional)
| Parameter | Type | Mode | Description |
|---|---|---|---|
qualified_name | VARCHAR | Positional |
Returns
Description
Removes the module part from a fully qualified name
Examples
SELECT inflector_demodulize('ActiveRecord::CoreExtensions::String'); Output
| inflector_demodulize('ActiveRecord::CoreExtensions::String') |
|---|
| String |
inflector_deordinalize
Signature
Parameters (Positional)
| Parameter | Type | Mode | Description |
|---|---|---|---|
ordinal | VARCHAR | Positional |
Returns
Description
Removes the ordinal suffix from a string (1st -> 1)
Examples
SELECT inflector_deordinalize('1st'); Output
| inflector_deordinalize('1st') |
|---|
| 1 |
inflector_is_camel_case
Signature
Parameters (Positional)
| Parameter | Type | Mode | Description |
|---|---|---|---|
text | VARCHAR | Positional |
Returns
Description
Returns true if the string is in camelCase format
Examples
SELECT inflector_is_camel_case('helloWorld'); Output
| inflector_is_camel_case('helloWorld') |
|---|
| true |
inflector_is_class_case
Signature
Parameters (Positional)
| Parameter | Type | Mode | Description |
|---|---|---|---|
text | VARCHAR | Positional |
Returns
Description
Returns true if the string is in ClassCase (PascalCase) format
Examples
SELECT inflector_is_class_case('HelloWorld'); Output
| inflector_is_class_case('HelloWorld') |
|---|
| true |
inflector_is_foreign_key
Signature
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
SELECT inflector_is_foreign_key('message_id'); Output
| inflector_is_foreign_key('message_id') |
|---|
| true |
inflector_is_kebab_case
Signature
Parameters (Positional)
| Parameter | Type | Mode | Description |
|---|---|---|---|
text | VARCHAR | Positional |
Returns
Description
Returns true if the string is in kebab-case format
Examples
SELECT inflector_is_kebab_case('hello-world'); Output
| inflector_is_kebab_case('hello-world') |
|---|
| true |
inflector_is_pascal_case
Signature
Parameters (Positional)
| Parameter | Type | Mode | Description |
|---|---|---|---|
text | VARCHAR | Positional |
Returns
Description
Returns true if the string is in PascalCase format
Examples
SELECT inflector_is_pascal_case('HelloWorld'); Output
| inflector_is_pascal_case('HelloWorld') |
|---|
| true |
inflector_is_screamingsnake_case
Signature
Parameters (Positional)
| Parameter | Type | Mode | Description |
|---|---|---|---|
text | VARCHAR | Positional |
Returns
Description
Returns true if the string is in SCREAMING_SNAKE_CASE format
Examples
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
Convert to camelCase.
inflector_to_class_case
Convert to ClassCase (PascalCase singular).
inflector_to_foreign_key
Converts a class name to a foreign key column name
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_plural
Pluralize an English word — handles irregulars (person→people, mouse→mice).
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_singular
Singularize an English word.
inflector_to_snake_case
Convert to snake_case.
inflector_to_table_case
Rails-style 'table_case' — snake_case + plural.
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
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)
'[]' Practical Examples
Cookbook
Real-world recipes and patterns for common use cases.
Quick reference
| Need | Pick |
|---|---|
| Convert one string between cases | inflector_to_<case> or inflect(case_name, value) |
| Rewrite every key in a struct | inflect('snake', struct_col) |
| Rewrite every column of a query | SELECT * FROM inflect('snake', (FROM ...)) |
| Pluralize / singularize an English word | inflector_to_plural / inflector_to_singular |
| Rails-style table name from class name | inflector_to_table_case |
| Class name → foreign-key column | inflector_to_foreign_key |
| Detect what case a string is in | inflector_is_* predicates |
| Number → ordinal label | inflector_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
Platforms
Supported platform architectures
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.
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.