Add support for CHECK constraint in INSERT statement in engine and SPI#14964
Add support for CHECK constraint in INSERT statement in engine and SPI#14964ebyhr merged 2 commits intotrinodb:masterfrom
Conversation
|
CI hit #14441 |
core/trino-spi/src/main/java/io/trino/spi/connector/ConnectorTableSchema.java
Outdated
Show resolved
Hide resolved
core/trino-spi/src/main/java/io/trino/spi/connector/ConnectorTableMetadata.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
To use from StatementAnalyzer. I would add a new method in Metadata if it's better than here. Or are you suggesting moving to the under column?
There was a problem hiding this comment.
Feels like not belonging here at all. I'd like to understand this more.
There was a problem hiding this comment.
Feels like it needs to stay here (or we would have to remove almost all usages of getTableSchema and migrate them to getTableMetadata)
|
Do we want to enforce that the check expression does not contain subqueries? |
61cc472 to
1666575
Compare
|
I think so. Changed to deny an expression having subqueries. |
core/trino-spi/src/main/java/io/trino/spi/connector/ConnectorTableMetadata.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Feels like it needs to stay here (or we would have to remove almost all usages of getTableSchema and migrate them to getTableMetadata)
There was a problem hiding this comment.
analyzeRowFilter technically does more or less what we want -- it takes a String expression and produces an Expression.
However, the method name and checks done in that method (e.g. analysis.hasRowFilter(name, currentIdentity) or analysis.registerTableForRowFiltering(name, currentIdentity)) are not applicable to check constraints.
Let's rollback changes to analyzeRowFilter.
Let's extract common code for converting connector-provided String into Expression (with due verifications).
core/trino-main/src/main/java/io/trino/sql/analyzer/StatementAnalyzer.java
Outdated
Show resolved
Hide resolved
core/trino-main/src/main/java/io/trino/sql/analyzer/Analysis.java
Outdated
Show resolved
Hide resolved
core/trino-main/src/main/java/io/trino/sql/planner/LogicalPlanner.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
"Check constraint violation: " + predicate ?
what does eg PostgreSQL print?
what does MySQL print?
There was a problem hiding this comment.
Here's the error message.
PostgreSQL
tpch=# create table test (c1 int check (c1 > 0));
tpch=# insert into test values (-1);
ERROR: new row for relation "test" violates check constraint "test_c1_check"
DETAIL: Failing row contains (-1).
MySQL
mysql> create table test (c1 int, check (c1 > 0));
mysql> insert into test values (-1);
ERROR 3819 (HY000): Check constraint 'test_chk_1' is violated.
There was a problem hiding this comment.
I like ours better, since it mentions the check formula. (we don't have a check name, and maybe we don't need one for error reporting)
I don't think we could do this for row filters, but i think we can do this for check constraints.
There was a problem hiding this comment.
for inserts/update/merge, the name addRowConstraints would be better.
Row filters become "row constraints", that's fine.
(the opposite, calling "check constraints" a "row filter" doesn't sound right)
core/trino-spi/pom.xml
Outdated
There was a problem hiding this comment.
Can we simply keep the old constructor as deprecated?
core/trino-spi/src/main/java/io/trino/spi/connector/ConnectorTableMetadata.java
Outdated
Show resolved
Hide resolved
1666575 to
f361888
Compare
|
CI |
core/trino-spi/src/main/java/io/trino/spi/connector/ConnectorTableMetadata.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
I like ours better, since it mentions the check formula. (we don't have a check name, and maybe we don't need one for error reporting)
I don't think we could do this for row filters, but i think we can do this for check constraints.
There was a problem hiding this comment.
Let's make the new one experimental and remove deprecation. For now. We will add deprecation when the new one is no longer experimental.
a43263c to
d5193c1
Compare
|
Addressed comments and added tests for other statements, UPDATE, MERGE and DELETE. |
d11a4d6 to
850f38c
Compare
850f38c to
f68c4a9
Compare
|
Rebased on upstream to resolve conflicts. |
core/trino-spi/src/main/java/io/trino/spi/connector/ConnectorTableMetadata.java
Outdated
Show resolved
Hide resolved
plugin/trino-base-jdbc/src/main/java/io/trino/plugin/jdbc/DefaultJdbcMetadata.java
Outdated
Show resolved
Hide resolved
plugin/trino-phoenix5/src/main/java/io/trino/plugin/phoenix5/PhoenixMetadata.java
Outdated
Show resolved
Hide resolved
core/trino-main/src/test/java/io/trino/sql/query/TestCheckConstraint.java
Outdated
Show resolved
Hide resolved
core/trino-main/src/test/java/io/trino/sql/query/TestCheckConstraint.java
Outdated
Show resolved
Hide resolved
core/trino-spi/src/main/java/io/trino/spi/connector/ConnectorTableMetadata.java
Outdated
Show resolved
Hide resolved
core/trino-spi/src/main/java/io/trino/spi/connector/ConnectorTableMetadata.java
Outdated
Show resolved
Hide resolved
core/trino-spi/src/main/java/io/trino/spi/connector/ConnectorTableMetadata.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Why was checkConstraints added to this method? They don't belong here. This method is specifically about masks and filters on a source table, and check constraints are none of that. They don't even apply to a source table, only to the target of an insert or update.
There was a problem hiding this comment.
Revert this. This is spurious reuse of functionality. We don't need to add an abstraction for a "row constraint expression". It's a bit of a stretch to call row filters and check constraints "constraint expressions"
There was a problem hiding this comment.
There's nothing special about a "row constraint" in this method. If anything, it should be called coerceToBoolean, but it also feels like spurious reuse of code.
There was a problem hiding this comment.
As I mentioned above, row filters and check constraints are two difference concepts. Let's not mix them up.
core/trino-spi/src/main/java/io/trino/spi/connector/ConnectorTableMetadata.java
Outdated
Show resolved
Hide resolved
core/trino-spi/src/main/java/io/trino/spi/connector/ConnectorTableMetadata.java
Outdated
Show resolved
Hide resolved
core/trino-main/src/main/java/io/trino/sql/analyzer/StatementAnalyzer.java
Outdated
Show resolved
Hide resolved
|
Fixing CI failures. |
e6f2f22 to
a390038
Compare
|
@martint Thanks for your review. Addressed comments. |
a390038 to
58e4c54
Compare
|
Rebased on upstream to resolve conflicts. |
|
@martint Gentle reminder. |
1 similar comment
|
@martint Gentle reminder. |
58e4c54 to
7daee6c
Compare
|
Rebased on upstream to resolve conflicts. |
7daee6c to
9e28882
Compare
There was a problem hiding this comment.
My intellij formatter would put an empty line before @return line. Would yours too?
There was a problem hiding this comment.
My intellij formatter would put an empty line before @return line. Would yours too?
There was a problem hiding this comment.
"should not contain temporal expression"
or "should not be time-dependent"
There was a problem hiding this comment.
nit: can import isDeterministic statically
9e28882 to
8f35174
Compare
|
trino-redis hit #13779 |
|
I will merge this PR tomorrow if there's no objection. |
There was a problem hiding this comment.
Ok, that makes sense. Please add that short explanation to the commit message so it doesn't get lost.
There was a problem hiding this comment.
This is the wrong error code. Check constraints are not row filters.
There was a problem hiding this comment.
EXPRESSION_NOT_IN_DISTINCT is the wrong error code for this.
There was a problem hiding this comment.
Using filter for the argument name is a little misleading. Rename it to constraint
There was a problem hiding this comment.
What's "predicate transformation"?
There was a problem hiding this comment.
That's a function inserting an expression to fail when the constraint is violated. Do you have any suggestions for the new variable name?
private static Function<Expression, Expression> failIfCheckConstraintIsNotMet(Metadata metadata, Session session)
{
return predicate -> new IfExpression(
// When predicate evaluates to UNKNOWN (e.g. NULL > 100), it should not violate the check constraint.
new CoalesceExpression(predicate, TRUE_LITERAL),
TRUE_LITERAL,
new Cast(failFunction(metadata, session, CONSTRAINT_VIOLATION, "Check constraint violation: " + predicate), toSqlType(BOOLEAN)));
}There was a problem hiding this comment.
I'm not sure that abstraction is even necessary. Why not inline that logic directly in addCheckConstraints?
There was a problem hiding this comment.
I.e., just modify addCheckConstraints to do this:
public RelationPlan addCheckConstraints(List<Expression> constraints, Table node, RelationPlan plan, Function<Table, Scope> accessControlScope)
{
...
for (Expression constraint : constraints) {
...
Expression predicate = new IfExpression(
// When predicate evaluates to UNKNOWN (e.g. NULL > 100), it should not violate the check constraint.
new CoalesceExpression(coerceIfNecessary(analysis, constraint, planBuilder.rewrite(constraint)), TRUE_LITERAL),
TRUE_LITERAL,
new Cast(failFunction(plannerContext.getMetadata(), session, CONSTRAINT_VIOLATION, "Check constraint violation: " + predicate), toSqlType(BOOLEAN)));
planBuilder = planBuilder.withNewRoot(new FilterNode(
idAllocator.getNextId(),
planBuilder.getRoot(),
predicate));
}
...
}There was a problem hiding this comment.
Similarly as above, the name filters is misleading. Rename it to constraints.
There was a problem hiding this comment.
assertThat(assertions.query(...))
.matches(...)
There was a problem hiding this comment.
Format as:
assertThatThrownBy(() -> assertions.query("""
MERGE INTO mock.tiny.nation
USING (VALUES 42) t(dummy)
ON false
WHEN NOT MATCHED THEN
INSERT VALUES (101, 'POLAND', 0, 'No comment')
"""))
.hasMessage("line 1:1: Cannot merge into a table with check constraints");8f35174 to
20319f5
Compare
|
@martint Addressed comments. |
20319f5 to
aa09214
Compare
There was a problem hiding this comment.
assertThat(assertions.query(...), here and everywhere else in this file
The same Scope instance should be used between row filters and check constraints in StatementAnalyzer. Otherwise, PlanSanityChecker throws "Unexpected identifier in logical plan" error.
aa09214 to
cf39155
Compare
Description
Add support for
CHECKconstraint inINSERTstatement to engine and SPI.Release notes
(x) Release notes are required, with the following suggested text: