typeorm-aurora-data-api-driver icon indicating copy to clipboard operation
typeorm-aurora-data-api-driver copied to clipboard

Cannot save null values: Cannot convert undefined or null to object

Open clalexander opened this issue 3 years ago • 8 comments

Issue

Cannot save null values to mysql database. Null query parameter values fail to map correctly in normalizeParams, resulting in TypeError: Cannot convert undefined or null to object

Stack Trace

TypeError: Cannot convert undefined or null to object
    at Function.keys (<anonymous>)
    at app/node_modules/typeorm-aurora-data-api-driver/dist/typeorm-aurora-data-api-driver.umd.js:440
    at Array.reduce (<anonymous>)
    at normalizeParams (app/node_modules/typeorm-aurora-data-api-driver/dist/typeorm-aurora-data-api-driver.umd.js:437)
    at query (app/node_modules/typeorm-aurora-data-api-driver/dist/typeorm-aurora-data-api-driver.umd.js:688)
    at Object.query (app/node_modules/typeorm-aurora-data-api-driver/dist/typeorm-aurora-data-api-driver.umd.js:908)
    at DataApiDriver.<anonymous> (app/node_modules/typeorm-aurora-data-api-driver/dist/typeorm-aurora-data-api-driver.umd.js:1344)
    at step (app/node_modules/typeorm-aurora-data-api-driver/dist/typeorm-aurora-data-api-driver.umd.js:81)
    at Object.next (app/node_modules/typeorm-aurora-data-api-driver/dist/typeorm-aurora-data-api-driver.umd.js:62)
    at app/node_modules/typeorm-aurora-data-api-driver/dist/typeorm-aurora-data-api-driver.umd.js:55
    at new Promise (<anonymous>)
    at __awaiter (app/node_modules/typeorm-aurora-data-api-driver/dist/typeorm-aurora-data-api-driver.umd.js:51)
    at DataApiDriver.query (app/node_modules/typeorm-aurora-data-api-driver/dist/typeorm-aurora-data-api-driver.umd.js:1336)
    at AuroraDataApiQueryRunner.<anonymous> (app/node_modules/typeorm/driver/aurora-data-api/AuroraDataApiQueryRunner.js:174)
    at step (app/node_modules/typeorm/node_modules/tslib/tslib.js:141)
    at Object.next (app/node_modules/typeorm/node_modules/tslib/tslib.js:122)

Steps to Reproduce

  1. Save an entity with a field that has a null value.

Package versions and database engine type:

  • Database Engine: [mysql 5.7]
  • TypeORM Version: [0.2.30]
  • Driver Version [2.1.0]

Additional Context

  • NestJS [^7.0.0]
  • NestJS Typeorm [7.1.5]

Debug Tracing

First, src/query-transformer/mysql-query-transformer.ts:127 transformParameters(parameters?: any[]) maps all null (and undefined) parameters to null, and not to the object of type { name: ``param_${index}``, value: parameter }.

Then in normalizeParams in dist/typeorm-aurora-data-api.umd.js:

const normalizeParams = params => params.reduce((acc, p) =>
  Array.isArray(p) ? acc.concat([normalizeParams(p)])
  : (
    (Object.keys(p).length === 2 && p.name && p.value !== 'undefined') ||
    (Object.keys(p).length === 3 && p.name && p.value !== 'undefined' && p.cast)
  ) ? acc.concat(p)
    : acc.concat(splitParams(p))
, []);

which takes the transformed parameters from above into params. It expects each element to be either an array or an object with two or three keys. When p is null, Object.keys(p) throws the error TypeError: Cannot convert undefined or null to object.

Because the mysql query transformer transformParameters returns null for null parameter values, it breaks in normalizeParams.

Suggested Fix

At src/query-transformer/mysql-query-transformer.ts:127, make the following changes:

protected transformParameters(parameters?: any[]) {
    if (!parameters) {
      return parameters
    }

    const expandedParameters = this.expandArrayParameters(parameters)

    return expandedParameters.map((parameter, index) => {
      if (parameter === undefined) { // << remove parameter === null
        return parameter
      }

      if (typeof parameter === 'object' && parameter !== null && parameter.value) { // << add parameter !== null
        return ({
          name: `param_${index}`,
          ...parameter,
        })
      }

      return {
        name: `param_${index}`,
        value: parameter, // << value here will be null for null parameters (works in my testing)
      }
    })
  }

clalexander avatar May 10 '21 21:05 clalexander

Hi, I'm wondering if I've had the same bug or if it's just me doing things wrong (I'm new to typeorm and the aurora data api driver).

I have a one to one relationship between two tables. In a simplified version:

@Entity()
export class UserEntity {
    @PrimaryColumn()
    email: string;

    @OneToOne(() => RefreshTokenEntity, refreshToken => refreshToken.user, {nullable: true})
    @JoinColumn()
    refreshToken: RefreshTokenEntity;
}
@Entity()
export class RefreshTokenEntity {
  @PrimaryColumn()
  hashedSignature: string;

  @OneToOne(() => UserEntity, user => user.refreshToken)
  user: UserEntity
}

I'm trying to delete any row in RefreshTokenEntity linked to a specific user. As per typeorm documentation, I'm using set(null) to do this:

        .createQueryBuilder()
        .relation(UserEntity, "refreshToken")
        .of(user)
        .set(null);

However I get this error (the stack trace looks similar to the one you have @clalexander):

    at Function.keys (<anonymous>)
    at /var/task/node_modules/typeorm-aurora-data-api-driver/dist/typeorm-aurora-data-api-driver.umd.js:440:17
    at Array.reduce (<anonymous>)
    at normalizeParams (/var/task/node_modules/typeorm-aurora-data-api-driver/dist/typeorm-aurora-data-api-driver.umd.js:437:46)
    at query (/var/task/node_modules/typeorm-aurora-data-api-driver/dist/typeorm-aurora-data-api-driver.umd.js:687:26)
    at Object.query (/var/task/node_modules/typeorm-aurora-data-api-driver/dist/typeorm-aurora-data-api-driver.umd.js:907:26)
    at DataApiDriver.<anonymous> (/var/task/node_modules/typeorm-aurora-data-api-driver/dist/typeorm-aurora-data-api-driver.umd.js:1340:62)
    at step (/var/task/node_modules/typeorm-aurora-data-api-driver/dist/typeorm-aurora-data-api-driver.umd.js:81:27)
    at Object.next (/var/task/node_modules/typeorm-aurora-data-api-driver/dist/typeorm-aurora-data-api-driver.umd.js:62:57)
    at /var/task/node_modules/typeorm-aurora-data-api-driver/dist/typeorm-aurora-data-api-driver.umd.js:55:75

Thanks for your insight!

Arnaud-DT avatar May 25 '21 20:05 Arnaud-DT

@ArnaudDutant Looks to be the same error to me.

clalexander avatar May 27 '21 18:05 clalexander

Should be fixed in 2.1.1, please let me know if you have more issues with it.

ArsenyYankovsky avatar May 29 '21 12:05 ArsenyYankovsky

Still seeing this error when used with Postgres. @ArsenyYankovsky

Looking into it now. My first assumption is that you should be able to pass in undefined as a parameter and the query should basically just drop that param. i.e. do not set it to null. However, it seems like the undefined param is getting passed all the way day to the data API lib and then crashes there.

I'm not sure if the driver should be dealing with this, or if it's upstream in TypeORM. My guess is that it should be getting taken care of upstream. So that by the time we get the query string and parameters it is already omited. I could be wrong though.

Thoughts?

seawatts avatar Jun 16 '21 01:06 seawatts

I think we need to check where undefined is handled when using the "normal" MySQL/Postgres drivers and try to replicate similar behavior.

ArsenyYankovsky avatar Jun 19 '21 12:06 ArsenyYankovsky

For me with version 2.2.0 passing a null value does work (param 3):

image

So I'm not sure what the issue could be for you @seawatts.

harm-less avatar Jul 13 '21 09:07 harm-less

I also faced this issue. At least it would be nice to have some proper error description, like what field goes wrong etc...

qstyler avatar Nov 02 '21 08:11 qstyler

I face a similar issue with "typeorm-aurora-data-api-driver": "2.3.4" and mysql.

I can't save empty set.

  @Column({ type: 'set', enum: enumValues(NetworkType)})
  networkType: NetworkType[];

An empty array [] is transformed in empty string '' by typeorm. The parameter is { value: '' } which fails this condition https://github.com/ArsenyYankovsky/typeorm-aurora-data-api-driver/blob/c01d226382ff700f7986f75ae3078ef6aa7ce92a/src/query-transformer/mysql-query-transformer.ts#L154

The resulting parameter is

{
 name: "param_1",
 value: {
   value: '',
 },
}

which breaks later because it is invalid

console.error                                                                                                                                                                                                    
    Error: 'param_1' is an invalid type                                                                                                                                                                            
        at error (node_modules/typeorm-aurora-data-api-driver/node_modules/data-api-client/index.js:39:35)                                                                      
        at formatType (node_modules/typeorm-aurora-data-api-driver/node_modules/data-api-client/index.js:215:4)                                                                 
        at formatParam (node_modules/typeorm-aurora-data-api-driver/dist/typeorm-aurora-data-api-driver.umd.js:481:48)                                                          
        at node_modules/typeorm-aurora-data-api-driver/node_modules/data-api-client/index.js:136:20                        

CorentinDoue avatar Dec 29 '21 11:12 CorentinDoue