New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Json processing in postgres notation #1810
base: master
Are you sure you want to change the base?
Conversation
…o json-postgres
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for working on this feature.
I didn't check all changes yet, so it's not a complete review.
H2 should work without any third-party libraries. JSON data type may not work without them, but all other features should not require them.
Also it's not a good idea to allow usage of third-party data types in getObject()
/ setObject()
/ etc., Jakson may be removed and replaced with something else. Perhaps JSON should be mapped to String
or byte[]
. If you need such support, reimplement it with a reflection.
throw getSyntaxError(); | ||
} | ||
int len = args.length; | ||
Value[] arr = (Value[]) Array.newInstance(Value.class, len); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
new Value[len]
String[] args; | ||
if(param.startsWith("{") && param.endsWith("}")) { | ||
param = param.substring(1, param.length() - 1); | ||
args = param.replaceAll(" ", "").split(","); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think that Parser
is a good place for such logic.
int len = args.length; | ||
Value[] arr = (Value[]) Array.newInstance(Value.class, len); | ||
for (int i = 0; i < len; i++) { | ||
Value v = StringUtils.isNumber(args[i]) ? ValueInt.get(new Integer(args[i])) : ValueString.get(args[i]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do not use new Integer(String)
, it is deprecated.
@@ -1759,6 +1788,212 @@ protected Value getValueWithArgs(Session session, Expression[] args) { | |||
String msgText = v1.getString(); | |||
throw DbException.fromUser(sqlState, msgText); | |||
} | |||
case JSON_FIELD: { | |||
if(v0.getValueType() == Value.JSON) { | |||
JsonNode json = ((ValueJson) v0).getObject(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All such code with third-party classes should be isolated into separate methods or classes.
@@ -1112,6 +1151,8 @@ public static int getTypeFromClass(Class <?> x) { | |||
return Value.TIMESTAMP; | |||
} else if (LocalDateTimeUtils.OFFSET_DATE_TIME == x || LocalDateTimeUtils.INSTANT == x) { | |||
return Value.TIMESTAMP_TZ; | |||
} else if (com.fasterxml.jackson.databind.JsonNode.class == x) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do not create hard run-time dependencies of third-party libraries.
@Override | ||
public int compareTypeSafe(Value v, CompareMode mode) { | ||
// TODO Auto-generated method stub | ||
return 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method should be implemented.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed here
@@ -0,0 +1,50 @@ | |||
SELECT CAST('{"tag1":"simple string"}' AS JSON); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing copyright header.
private static final ObjectMapper mapper = new ObjectMapper(); | ||
|
||
ValueJson(String str) throws IOException { | ||
int memFirst = Utils.getMemoryUsed(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do not use this method in the main code, it's not reliable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not entirely clear how to evaluate various json objects.
Here used string length, but it may work not always.
this.json=mapper.createObjectNode(); | ||
this.string = this.json.toString(); | ||
} | ||
this.string = str.replaceAll("\n", ""); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps JSON should be fully normalized into some canonical form.
@@ -427,7 +427,7 @@ private void process(String sql, boolean allowReconnect) throws Exception { | |||
if (statements != null) { | |||
statements.add(sql); | |||
} | |||
if (sql.indexOf('?') == -1) { | |||
if (sql.indexOf('?') == -1 || sql.indexOf("?'") != -1 || sql.indexOf("?|") != -1 || sql.indexOf("?&") != -1) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Such check is not reliable. Characters next to ?
should be tested instead. Not very critical for now, but at least a TODO comment should be placed here to indicate a problem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Current check used to exclude collision between JSON operators and parameter token because it's not obvious how this can be resolved at the parser level.
/** | ||
* The PostgreSQL token "||" | ||
*/ | ||
private static final int JSON_CONCAT = JSON_EXISTS_ALL + 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How it is distinguished from a string ||
concatenation and why these constants are not consistent with TOKENS
array?
addFunction("JSON_EXISTS_ANY", JSON_EXISTS_ANY, 2, Value.BOOLEAN); | ||
addFunction("JSON_EXISTS_ALL", JSON_EXISTS_ALL, 2, Value.BOOLEAN); | ||
addFunction("JSON_CONCAT", JSON_CONCAT, 2, Value.JSON); | ||
addFunction("JSON_DELETE_FIELD", JSON_DELETE_FIELD, 2, Value.JSON); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where all these functions came from? Neither SQL Standard nor PostgreSQL documentation have them with an exception for JSON_EXISTS
that is described in the standard (but I'm not sure that it is compatible with your implementation).
If you need them to support some operations, it's better to use own class similar to BinaryOperation
instead. Operations can be mapped to functions only when a similar function exists.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, now these functions are used for the work of operators. With the development of the functionality, it is planned to add new ones, but I'll try to correlate names with the standard or the PostgreSQL dialect.
JSON support was standardized in SQL:2016, but PostgreSQL is not compatible with the standard. I wonder how many complexity these weird PostgreSQL-only operations create for us when we decide to support standard features.
|
@lazarevnik Thank you very much for this! @katzyn I don't really like adding these operators, I'd prefer functions, they are much easier to extend and much less likely to cause trouble with parsing. @lazarevnik Are you deliberately aiming at Postgresql compatibilty? |
@katzyn Thank you for the review, it is very helpful. |
There is a SQL Standard. Why you need non-standard syntax for Ignite? Sorry, I don't understand it. H2 is not a PostgreSQL emulator. Many databases have own legacy syntax constructions in different areas, but usually they begin to support standard features too. |
I want to work towards caching with read-/write-through. Ignite allows you to use SQL queries for this on a par to ordinary cache mechanism. So, to work with not abstract standard, but with concrete RDBMS, I used PostgreSQL syntax and operators. |
It means that you don't have a real reason, because you're not restricted to that proprietary syntax. Oracle uses the standard, MySQL partially compatible with the standard, there were some attempts to made PostgreSQL compatible with the standard in 2017, but it looks like they aren't yet merged. I don't understand why you didn't discuss your addition with H2 community earlier, but what's done is done.
Could you extract |
Yes, I'll do it in near future |
Here new pull request. |
<dependency> | ||
<groupId>com.fasterxml.jackson.core</groupId> | ||
<artifactId>jackson-annotations</artifactId> | ||
<version>2.9.8</version> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Q: probably it will be good to change scope to provided for this dependencies, to avoid dependency conflicts
<version>2.9.8</version> | |
<version>2.9.8</version> | |
<scope>provided</scope> |
Class<?> j; | ||
try { | ||
j = JdbcUtils.loadUserClass(JSON_CLASS_NAME); | ||
} catch (Exception e) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please catch exceptions which you expect but not so big as this one
Hello!
Here I've implemented :
Also here are some queries shows how it works.
Unfortunately, I've got some problems with memory and network tests.
I'll glad to discuss their decision.