fix: Add JSON column type for SQL Views#23773
Conversation
fc0a2da to
b0b4635
Compare
|
We discussed this in todays platform meeting and we are happy with this approach but it is a breaking change for anyone that has views extracting JSON columns where the response changes from quoted JSON to plain JSON. Before we do that we need the OK from @stian-sandvold |
|
Thanks @jbee . Yes, I think we can maybe include this in the release notes. My feeling is that not many people are using JSON in SQL views, but you never know. Regardless,I think it should be a one line fix on the client side, so even if it does break existing integrations/scripts, it should be a minor fix on the client side. Will wait for @stian-sandvold to weigh in. |
stian-sandvold
left a comment
There was a problem hiding this comment.
I guess my main questions here are:
- Why do we need to make this change, when it's already parsable. Is there some specific requirement for this? Since it's potentially breaking clients already expected a parsable to json string.
- I saw other custom types mentioned as well, like geometry. Is the plan to add custom handling for those as well?
- If we do have a plan to decouple from Grid/analytics, is there any reason to rush this solution in now, instead of working towards a long term solution?
Assuming its valuable to bring this in now, I am for it (Note the other comments); Just want to understand the need and intention :)
| for (int i = 1; i <= cols; i++) { | ||
| Object value = rs.getObject(i); | ||
| grid.addValue(isJson[i] && value != null ? new RawJsonValue(value.toString()) : value); | ||
| if (maxLimit > 0 && i > maxLimit) { |
There was a problem hiding this comment.
Looks like this is a bug that's already present, but caught my attention looking at the code:
i represent the number of columns, while the exception highlights the number of rows.
This is however also the same problem in the old code (addRows).
| SqlRowSetMetaData rsmd = rs.getMetaData(); | ||
| int cols = rsmd.getColumnCount(); | ||
| boolean[] isJson = new boolean[cols + 1]; | ||
| for (int i = 1; i <= cols; i++) { |
There was a problem hiding this comment.
Just to keep the code a bit more condense, you could move the content of this for loop into the next, and keep everything in one, instead of spreading the column related code in two blocks. Performance wise, I don't see any advantage of pre-calculating these booleans
| * or is null it will return the given parameter object itself | ||
| */ | ||
| public static Object maybeFormat(final Object object) { | ||
| if (object instanceof RawJsonValue json) { |
There was a problem hiding this comment.
from this PR, as far as I can tell in the code, this piece of code is redundant.
Since you're already adding a condition in JacksonRowDataSerializer to not use maybeFormat if the field is an instanceof RawJsonValue, you will in theory never get it in this code execution.
I'd say remove either or, unless there's some reason I cant see we need both checks :)
This came up because I was working on a custom SQL view for an integration script and noticed that the JSON column was not really "JSON". It is as you say, trivial for the client to deal with this, it's just not really that nice.
No plans for this at the moment with this PR. It could of course be nice to be able to do this. I think we would have to fully decouple Grid first though.
I do not think there is really a rush. Its more that it just is wrong. :-D
Let me know how you want to proceed @stian-sandvold . |
|
|
Let's go ahead with it, but we'll have to share this in release notes since it potentially breaks clients using sql views with jsonb :) |
|



Fixes https://dhis2.atlassian.net/browse/DHIS2-21373
Root cause
SqlRowSet.getObject(i)returns a PGobject for PostgreSQL json/jsonb columns.PGobject.toString()returns the JSON text.JacksonRowDataSerializerthen callsjgen.writeString()on every grid cell unconditionally, quoting and escaping the already-valid JSON text.Proposed fix
A
RawJsonValuemarker record is introduced in dhis-api.HibernateSqlViewStore.populateRows()inspectsSqlRowSetMetaData.getColumnTypeName()and wraps non-null json/jsonb values asRawJsonValue.JacksonRowDataSerializeremitsRawJsonValueviajgen.writeRawValue()instead ofjgen.writeString().For CSV/XML outputs,
OutputFormatter.maybeFormat()unwrapsRawJsonValueto its plain string.This is a workaround, not a proper fix. See the companion architectural ticket (Ticket 2 below) for why.
Limitations of the approach
RawJsonValue leaks a SQL-view-specific concept into dhis-api (JacksonRowDataSerializer, OutputFormatter), both of which are shared by analytics and other
Gridconsumers.CSV and XML outputs emit the raw JSON string inline, which may not be valid for those formats depending on consumer expectations.
Obviously breaks existing clients which may unescape JSON output.
Before:
After: