Skip to content

Commit

Permalink
fix(core): ignore SQL convertor methods on object embeddables
Browse files Browse the repository at this point in the history
Closes #4824
  • Loading branch information
B4nan committed Oct 15, 2023
1 parent 9accdf6 commit 92e1d6f
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 2 deletions.
2 changes: 1 addition & 1 deletion packages/knex/src/AbstractSqlDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1055,7 +1055,7 @@ export abstract class AbstractSqlDriver<Connection extends AbstractSqlConnection
protected buildFields<T extends object>(meta: EntityMetadata<T>, populate: PopulateOptions<T>[], joinedProps: PopulateOptions<T>[], qb: QueryBuilder<T>, fields?: Field<T>[]): Field<T>[] {
const lazyProps = meta.props.filter(prop => prop.lazy && !populate.some(p => p.field === prop.name || p.all));
const hasLazyFormulas = meta.props.some(p => p.lazy && p.formula);
const requiresSQLConversion = meta.props.some(p => p.customType?.convertToJSValueSQL);
const requiresSQLConversion = meta.props.some(p => p.customType?.convertToJSValueSQL && p.persist !== false);
const hasExplicitFields = !!fields;
const ret: Field<T>[] = [];

Expand Down
2 changes: 1 addition & 1 deletion packages/knex/src/query/QueryBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -946,7 +946,7 @@ export class QueryBuilder<T extends object = AnyEntity> {

const meta = this.mainAlias.metadata;
/* istanbul ignore next */
const requiresSQLConversion = meta?.props.filter(p => p.hasConvertToJSValueSQL) ?? [];
const requiresSQLConversion = meta?.props.filter(p => p.hasConvertToJSValueSQL && p.persist !== false) ?? [];

if (this.flags.has(QueryFlag.CONVERT_CUSTOM_TYPES) && (fields.includes('*') || fields.includes(`${this.mainAlias.aliasName}.*`)) && requiresSQLConversion.length > 0) {
requiresSQLConversion.forEach(p => ret.push(this.helper.mapper(p.name, this.type)));
Expand Down
91 changes: 91 additions & 0 deletions tests/features/embeddables/GH4824.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { Embeddable, Embedded, Entity, PrimaryKey, Property, Type } from '@mikro-orm/core';
import { MikroORM } from '@mikro-orm/sqlite';

class Point {

public latitude!: number;
public longitude!: number;

constructor(latitude: number, longitude: number) {
this.latitude = latitude;
this.longitude = longitude;
}

}

class PointType extends Type<Point | undefined, string | undefined> {

convertToDatabaseValue(value: Point | undefined): string | undefined {
if (!value) {
return value;
}

return `point(${value.longitude} ${value.latitude})`;
}

convertToJSValue(value: string | undefined): Point | undefined {
const m = value?.match(/point\((-?\d+(\.\d+)?) (-?\d+(\.\d+)?)\)/i);

if (!m) {
return undefined;
}

return new Point(+m[3], +m[1]);
}

convertToJSValueSQL(key: string) {
return `ST_AsText(${key})`;
}

convertToDatabaseValueSQL(key: string) {
return `ST_GeomFromText(${key}, 4326)`;
}

getColumnType(): string {
return 'geometry';
}

}

@Embeddable()
class Address {

@Property()
postalCode!: string;

@Property({ type: PointType, nullable: true })
geolocation?: Point;

}

@Entity()
class User {

@PrimaryKey()
id!: number;

@Embedded(() => Address, { array: true })
addresses: Address[] = [];

}

let orm: MikroORM;

beforeAll(async () => {
orm = await MikroORM.init({
entities: [User],
dbName: ':memory:',
});
await orm.schema.createSchema();
});

afterAll(async () => {
await orm.close(true);
});

test('GH #4824', async () => {
const user = await orm.em.findOne(User, {
id: 1,
});
expect(user).toBeNull();
});

0 comments on commit 92e1d6f

Please sign in to comment.