objection.js
objection.js copied to clipboard
knex.migrate.latest() fails when ...knexSnakeCaseMappers is part of the knex config
I'm having the following issue:
When trying to do migration with an "knexSnakeCaseMappers"-ized knex instance using "objection": "3.0.1"
and knex": "^0.95.15"
, the migration fails with the following error:
migration file "1.ts" failed
migration failed with error: Cannot read properties of undefined (reading 'lastIndexOf')
TypeError {
message: 'Cannot read properties of undefined (reading \'lastIndexOf\')',
}
› node_modules/objection/lib/utils/identifierMapping.js:139:21
› node_modules/objection/lib/utils/identifierMapping.js:13:16
› Object.wrapIdentifier (node_modules/objection/lib/utils/identifierMapping.js:180:23)
› Client_SQLite3.customWrapIdentifier (node_modules/knex/lib/client.js:177:26)
› TableCompiler_SQLite3.primary (node_modules/knex/lib/dialects/sqlite3/schema/sqlite-tablecompiler.js:192:34)
› TableCompiler_SQLite3.alterTable (node_modules/knex/lib/schema/tablecompiler.js:245:32)
› TableCompiler_SQLite3.create (node_modules/knex/lib/schema/tablecompiler.js:59:10)
› TableCompiler_SQLite3.toSQL (node_modules/knex/lib/schema/tablecompiler.js:39:22)
› TableBuilder.toSQL (node_modules/knex/lib/schema/tablebuilder.js:49:44)
› SchemaCompiler_SQLite3.build (node_modules/knex/lib/schema/compiler.js:143:23)
How to reproduce:
The error is thrown when running ts-node migrate.ts
// migrate.ts
import { db } from '../src/db
db.migrate.latest() // <--- This command fails
// db.ts
import { knexSnakeCaseMappers } from 'objection'
export const db = knex({
client: 'sqlite3',
migrations: { tableName: 'knex_migrations', },
connection: { filename: './sqlite.db' },
useNullAsDefault: true,
...knexSnakeCaseMappers(),
})
// migrations/1.ts
import { Knex } from 'knex'
export function up(knex: Knex) {
return knex.schema.createTable('example', (table) => {
table.uuid('id').primary()
})
}
export function down(knex: Knex) {
return knex.schema.dropTable('example')
}
Possible solution
The issue is that undefined
is passed as the value for the str
parameter to the function that is returned from mapLastPart
. I could fix the issue by returning the identity whenever str
is not a String
// node_modules/objection/lib/utils/identifierMapping.js
function mapLastPart(mapper, separator) {
return (str) => {
// if (typeof str !== 'string' ) { return str } // <--- Potential fix
const idx = str.lastIndexOf(separator);
const mapped = mapper(str.slice(idx + separator.length));
return str.slice(0, idx + separator.length) + mapped;
};
}
I have the same problem when I use knexSnakeCaseMappers with sqlite3 and better-sqlite3 and this error throw on setting non increment primary key like:
table.string('id').notNullable();
table.string('name').notNullable();
table.integer('channel_id');
table.integer('project_id').notNullable();
table.specificType('status', 'account_statuses');
table.primary(['id', 'name']);
table.unique(['id', 'name']);
table.foreign('project_id').references('id').inTable('projects');
table.foreign('channel_id').references('id').inTable('channels');
I have the same problem when I use knexSnakeCaseMappers with sqlite3 and better-sqlite3 and this error throw on setting non increment primary key like:
table.string('id').notNullable(); table.string('name').notNullable(); table.integer('channel_id'); table.integer('project_id').notNullable(); table.specificType('status', 'account_statuses'); table.primary(['id', 'name']); table.unique(['id', 'name']); table.foreign('project_id').references('id').inTable('projects'); table.foreign('channel_id').references('id').inTable('channels');
Have the same issue with the same use case. Any update on this?
@DomenicoCasillo In my case, the solution was to set the primary key constraint name like table.primary(['id', 'channel_id'], 'accounts_pkey');
@DomenicoCasillo In my case, the solution was to set the primary key constraint name like
table.primary(['id', 'channel_id'], 'accounts_pkey');
Unfortunately this didn't work for me. My final solution to the issue is add knexSnakeCaseMappers to knexfile conditionally. For instance it's not added only when migrate is running (you can use an env var for this).
Also having this issue. Applying this patch fixes it for me:
diff --git a/node_modules/objection/lib/utils/identifierMapping.js b/node_modules/objection/lib/utils/identifierMapping.js
index afec0bc..9b6e8d5 100644
--- a/node_modules/objection/lib/utils/identifierMapping.js
+++ b/node_modules/objection/lib/utils/identifierMapping.js
@@ -135,6 +135,7 @@ function isDigit(char) {
// If no separators are found, `mapper` is called for the entire string.
function mapLastPart(mapper, separator) {
return (str) => {
+ if (typeof str !== 'string') return str;
const idx = str.lastIndexOf(separator);
const mapped = mapper(str.slice(idx + separator.length));
return str.slice(0, idx + separator.length) + mapped;
This was fixed in #2333