dmfay / massive-js

A data mapper for Node.js and PostgreSQL.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

JS Date to timestamptz casting

atistler opened this issue · comments

db > db.instances.find({'sys_period @>': new Date()})
error: malformed range literal: "2018-04-08T15:31:11.233-04:00"
    at Connection.parseE (/Users/adamtistler/WebstormProjects/priapus/api/node_modules/pg/lib/connection.js:545:11)
    at Connection.parseMessage (/Users/adamtistler/WebstormProjects/priapus/api/node_modules/pg/lib/connection.js:370:19)
db > db.instances.where('sys_period @> $1', [new Date()])
error: malformed range literal: "2018-04-08T15:31:24.853-04:00"
    at Connection.parseE (/Users/adamtistler/WebstormProjects/priapus/api/node_modules/pg/lib/connection.js:545:11)
    at Connection.parseMessage (/Users/adamtistler/WebstormProjects/priapus/api/node_modules/pg/lib/connection.js:370:19)
db > db.instances.where('sys_period @> $1::timestamptz', [new Date()])

I thought that Date(s) were automatically cast as timestamptz? Is this behavior expected? I can cast the value myself, however i thought that the library was supposed to handle this.

It does for ordinary predicates, but you've got an array operation here and parameters get processed through the literalizeArray mutator which evidently doesn't handle date casts.

If you'd like to dig in and submit a pull request, feel free!

Looking through this, I am not sure that automatic casting is happening on standard queries: for example:

db.instances.find({'created_at <': new Date()})

LOG: statement: SELECT * FROM "instances" WHERE "created_at" < '2018-04-08T23:50:25.314-04:00' ORDER BY "id"

I looked at docGenerator/tableGenerator in where.js and it looks like value casting is only done in docGenerator and not for non 'doc' based predicates (tableGenerator).

I did attempt to modify mutators.js to handle Date values, however i ran into quoting issues:

exports.literalizeArray = condition => {
    if (_.isArray(condition.value)) {
    const sanitizedValues = condition.value.map(function (v) {
      if (_.isString(v) && (v === '' || v === 'null' || v.search(/[,{}\s\\"]/) !== -1)) {
        return `"${v.replace(/([\\"])/g, '\\$1')}"`;
      } else if (v === null) {
        return 'null';
      }

      return v;
    });

    condition.params.push(`{${sanitizedValues.join(',')}}`);
  } else if (_.isDate(condition.value)) {
    condition.params.push(`'${condition.value.toISOString()}'::timestamptz`)
  } else {
    condition.params.push(condition.value);
  }

  condition.value = `$${condition.offset}`;

  return condition;
};
db > db.instances.find({'sys_period @>': new Date()})
error: malformed range literal: "'2018-04-09T03:53:16.838Z'::timestamptz"

LOG: statement: SELECT * FROM "instances" WHERE "sys_period" @> '''2018-04-09T03:53:16.838Z''::timestamptz' ORDER BY "id"

Will try to dig into this more.

This appears to be a driver issue; node-pg casts Dates appropriately for most queries, but ranges other than numrange aren't currently supported. The casts in docGenerator are there to ensure fields in JSON documents are tested against the appropriate types, since they otherwise come out as text.