Skip to content

Save an array value to jsonb type column - #1432

Closed
lifubang wants to merge 1 commit into
brianc:masterfrom
lifubang:master
Closed

lifubang wants to merge 1 commit into
brianc:masterfrom
lifubang:master

Conversation

@lifubang

Copy link
Copy Markdown
CREATE TABLE public.jsontest (
	test jsonb NULL
)
WITH (
	OIDS=FALSE
);
pool.connect(function(errp, client, done) {
  client.query('INSERT INTO jsontest(test1) VALUES($1::jsonb);', [[{"id":1, "name":"LFB"}, {"id":2, "name":"ZHM"}]], function(e,d) {
      console.log(e);
      console.log(d);
      done();
  });
});

* It returns:
{ error: invalid input syntax for type json
    at Connection.parseE (E:\develop\nodejs\mongoOPLog\node_modules\pg\lib\connection.js:546:11)
    at Connection.parseMessage (E:\develop\nodejs\mongoOPLog\node_modules\pg\lib\connection.js:371:19)
    at Socket.<anonymous> (E:\develop\nodejs\mongoOPLog\node_modules\pg\lib\connection.js:114:22)
    at emitOne (events.js:96:13)
    at Socket.emit (events.js:188:7)
    at readableAddChunk (_stream_readable.js:176:18)
    at Socket.Readable.push (_stream_readable.js:134:10)
    at TCP.onread (net.js:547:20)
  name: 'error',
  length: 174,
  severity: 'ERROR',
  code: '22P02',
  detail: 'Expected ":", but found ",".',
  hint: undefined,
  position: undefined,
  internalPosition: undefined,
  internalQuery: undefined,
  where: 'JSON data, line 1: {"{\\"id\\":1,\\"name\\":\\"LFB\\"}",...',
  schema: undefined,
  table: undefined,
  column: undefined,
  dataType: undefined,
  constraint: undefined,
  file: 'json.c',
  line: '1151',
  routine: 'report_parse_error' }

You can fix it by:

pool.connect(function(errp, client, done) {
  client.query('INSERT INTO jsontest(test1) VALUES($1::jsonb);', [JSON.stringify([{"id":1, "name":"LFB"}, {"id":2, "name":"ZHM"}])], function(e,d) {
      console.log(e);
      console.log(d);
      done();
  });
});

But if modify the arrayString function in utils.js, It will be more convenient.

```sql
CREATE TABLE public.jsontest (
	test jsonb NULL
)
WITH (
	OIDS=FALSE
);
```

```js
pool.connect(function(errp, client, done) {
  client.query('INSERT INTO jsontest(test1) VALUES($1::jsonb);', [[{"id":1, "name":"LFB"}, {"id":2, "name":"ZHM"}]], function(e,d) {
      console.log(e);
      console.log(d);
      done();
  });
});

* It returns:
{ error: invalid input syntax for type json
    at Connection.parseE (E:\develop\nodejs\mongoOPLog\node_modules\pg\lib\connection.js:546:11)
    at Connection.parseMessage (E:\develop\nodejs\mongoOPLog\node_modules\pg\lib\connection.js:371:19)
    at Socket.<anonymous> (E:\develop\nodejs\mongoOPLog\node_modules\pg\lib\connection.js:114:22)
    at emitOne (events.js:96:13)
    at Socket.emit (events.js:188:7)
    at readableAddChunk (_stream_readable.js:176:18)
    at Socket.Readable.push (_stream_readable.js:134:10)
    at TCP.onread (net.js:547:20)
  name: 'error',
  length: 174,
  severity: 'ERROR',
  code: '22P02',
  detail: 'Expected ":", but found ",".',
  hint: undefined,
  position: undefined,
  internalPosition: undefined,
  internalQuery: undefined,
  where: 'JSON data, line 1: {"{\\"id\\":1,\\"name\\":\\"LFB\\"}",...',
  schema: undefined,
  table: undefined,
  column: undefined,
  dataType: undefined,
  constraint: undefined,
  file: 'json.c',
  line: '1151',
  routine: 'report_parse_error' }
```

You can fix it by:
```js
pool.connect(function(errp, client, done) {
  client.query('INSERT INTO jsontest(test1) VALUES($1::jsonb);', [JSON.stringify([{"id":1, "name":"LFB"}, {"id":2, "name":"ZHM"}])], function(e,d) {
      console.log(e);
      console.log(d);
      done();
  });
});
```

But if modify the arrayString function in utils.js, It will be more convenient.
@sehrope

sehrope commented Aug 29, 2017

Copy link
Copy Markdown
Contributor

If I'm understanding this PR correctly, you'd checking to see if the array has non-scalar children to determine that the user's intent was to treat it as JSON (as opposed to an array):

if (val && val.length > 0 && typeof(val[0]) === 'object' && typeof val[0].toPostgres != 'function') {
  return JSON.stringify(val);
} else {
  // old logic for handling arrays goes here
}

I don't think this is a good idea as it doesn't handle every situation. None of the following would be covered because the first element of the array is not an object:

[null]
[null, 1, 2, 3]
[null, {foo: 'bar'}]
[1, {foo: 'bar'}]
[false, {foo: 'bar'}]

Each of those can be serialized to valid JSON but wouldn't pass the initial check in the PR. To use a value like that you'd still have to manually serialize it to JSON prior to use.

We could check all values to see if they're all non-scalars but then you'd still have divergent behavior if an empty array is specified. Should [] be sent as an empty Postgres array, {}, or an empty JSON array, []?

Arrays have been a part of Postgres long before JSON, I think it's fair for them to have priority here. If someone is saving a JSON array (or an arbitrary JSON value which could be a string, number, or null), then they need to explicitly handle it.

@abenhamdine

abenhamdine commented Aug 29, 2017 •

Copy link
Copy Markdown
Contributor

I agree with @sehrope , I don't see any obvious way to distinguish arrays/json objects.
And you could also want to save a normal array in a jsonb column so it seems difficult to automatically apply the right format.
Personnaly, I indeed stringify specifically the object before passing it to the query.

Otherwise, it's better to open an issue when you think you enconter a bug : not everyone watchs the pull requests, so opening a bug allows users to easily find it & provide feedback if they want, thx !

@charmander

Copy link
Copy Markdown
Collaborator

As was said, this approach isn’t reliable. JSON.stringify is a workable workaround. See also #442.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants