Skip to content

Commit b470cbc

Browse files
authored
Prevent caching INSERT statements (#3212)
This disables adding `INSERT` statements to the plan cache. The main reason is related to potentially generating very large `QueryPhysicalPlan` objects due to the way we capture individual `INSERT` values and generate potentially massive `QueryPlanConstraint` to verify their type (unnecessarily), in addition to other overheads such as generating massive amount of redundant `Type` objects and `RCV`s if the insert is touching a very wide table with hundreds of rows. This resolves #3210
1 parent 94602f8 commit b470cbc

File tree

3 files changed

+65
-14
lines changed

3 files changed

+65
-14
lines changed

fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/AstNormalizer.java

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -311,9 +311,21 @@ public Object visitDdlStatement(@Nonnull RelationalParser.DdlStatementContext ct
311311
}
312312

313313
@Override
314-
public Object visitDmlStatement(@Nonnull RelationalParser.DmlStatementContext ctx) {
315-
queryCachingFlags.add(Result.QueryCachingFlags.IS_DML_STATEMENT);
316-
return visitChildren(ctx);
314+
public Object visitInsertStatement(final RelationalParser.InsertStatementContext ctx) {
315+
queryCachingFlags.add(Result.QueryCachingFlags.IS_INSERT_STATEMENT);
316+
return super.visitInsertStatement(ctx);
317+
}
318+
319+
@Override
320+
public Object visitUpdateStatement(final RelationalParser.UpdateStatementContext ctx) {
321+
queryCachingFlags.add(Result.QueryCachingFlags.IS_UPDATE_STATEMENT);
322+
return super.visitUpdateStatement(ctx);
323+
}
324+
325+
@Override
326+
public Object visitDeleteStatement(final RelationalParser.DeleteStatementContext ctx) {
327+
queryCachingFlags.add(Result.QueryCachingFlags.IS_DELETE_STATEMENT);
328+
return super.visitDeleteStatement(ctx);
317329
}
318330

319331
@Override
@@ -600,7 +612,9 @@ public static class Result {
600612
*/
601613
public enum QueryCachingFlags {
602614
IS_DDL_STATEMENT,
603-
IS_DML_STATEMENT,
615+
IS_UPDATE_STATEMENT,
616+
IS_DELETE_STATEMENT,
617+
IS_INSERT_STATEMENT,
604618
IS_DQL_STATEMENT,
605619
IS_UTILITY_STATEMENT,
606620
IS_ADMIN_STATEMENT,

fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/PlanGenerator.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,11 @@ private static IndexFetchMethod toRecLayerIndexFetchMethod(@Nullable Options.Ind
434434
*/
435435
private static boolean shouldNotCache(@Nonnull final Set<AstNormalizer.Result.QueryCachingFlags> queryCachingFlags) {
436436
return queryCachingFlags.contains(AstNormalizer.Result.QueryCachingFlags.WITH_NO_CACHE_OPTION) ||
437-
queryCachingFlags.contains(AstNormalizer.Result.QueryCachingFlags.IS_DDL_STATEMENT);
437+
queryCachingFlags.contains(AstNormalizer.Result.QueryCachingFlags.IS_DDL_STATEMENT) ||
438+
// avoid caching INSERT statements since they could result in extremely large plans leading to potential
439+
// OOM when too many of them are stored in the plan cache.
440+
queryCachingFlags.contains(AstNormalizer.Result.QueryCachingFlags.IS_INSERT_STATEMENT);
441+
438442
}
439443

440444
/**

fdb-relational-core/src/test/java/com/apple/foundationdb/relational/recordlayer/query/AstNormalizerTests.java

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -607,41 +607,74 @@ void parseDqlStatementWithLogQuerySetLogQueryFlag() throws Exception {
607607
}
608608

609609
@Test
610-
void parseDmlStatementWithDryRunSetDryRunFalse() throws Exception {
610+
void parseUpdateStatementWithoutDryRunSetsDryRunOptionsToFalse() throws Exception {
611611
validate(List.of("update A set A2 = 52 where A1 > 2"),
612612
PreparedParams.empty(),
613-
"update \"A\" set \"A2\" = ? where \"A1\" > ? ", // note: this is irrelevant as the query will be recompiled anyway.
613+
"update \"A\" set \"A2\" = ? where \"A1\" > ? ",
614614
List.of(Map.of(constantId(5), 52, constantId(9), 2)),
615615
null,
616616
-1,
617-
EnumSet.of(AstNormalizer.Result.QueryCachingFlags.IS_DML_STATEMENT),
617+
EnumSet.of(AstNormalizer.Result.QueryCachingFlags.IS_UPDATE_STATEMENT),
618618
Map.of(Options.Name.DRY_RUN, false));
619619
}
620620

621621
@Test
622-
void parseDmlStatementWithDryRunSetDryRunTrue() throws Exception {
622+
void parseUpdateStatementWithDryRunOptionSetsDryRunToTrue() throws Exception {
623623
validate(List.of("update A set A2 = 52 where A1 > 2 OPTIONS(DRY RUN)"),
624624
PreparedParams.empty(),
625-
"update \"A\" set \"A2\" = ? where \"A1\" > ? ", // note: this is irrelevant as the query will be recompiled anyway.
625+
"update \"A\" set \"A2\" = ? where \"A1\" > ? ",
626626
List.of(Map.of(constantId(5), 52, constantId(9), 2)),
627627
null,
628628
-1,
629-
EnumSet.of(AstNormalizer.Result.QueryCachingFlags.IS_DML_STATEMENT),
629+
EnumSet.of(AstNormalizer.Result.QueryCachingFlags.IS_UPDATE_STATEMENT),
630630
Map.of(Options.Name.DRY_RUN, true));
631631
}
632632

633633
@Test
634-
void parseDmlStatementWithMultipleQueryOptions() throws Exception {
634+
void parseUpdateStatementWithMultipleQueryOptions() throws Exception {
635635
validate(List.of("update A set A2 = 52 where A1 > 2 OPTIONS(DRY RUN, nocache, log query)"),
636636
PreparedParams.empty(),
637-
"update \"A\" set \"A2\" = ? where \"A1\" > ? ", // note: this is irrelevant as the query will be recompiled anyway.
637+
"update \"A\" set \"A2\" = ? where \"A1\" > ? ",
638638
List.of(Map.of(constantId(5), 52, constantId(9), 2)),
639639
null,
640640
-1,
641-
EnumSet.of(AstNormalizer.Result.QueryCachingFlags.IS_DML_STATEMENT, AstNormalizer.Result.QueryCachingFlags.WITH_NO_CACHE_OPTION),
641+
EnumSet.of(AstNormalizer.Result.QueryCachingFlags.IS_UPDATE_STATEMENT, AstNormalizer.Result.QueryCachingFlags.WITH_NO_CACHE_OPTION),
642642
Map.of(Options.Name.DRY_RUN, true, Options.Name.LOG_QUERY, true));
643643
}
644644

645+
@Test
646+
void parseUpdateStatementSetsQueryFlagCorrectly() throws Exception {
647+
validate(List.of("update A set A2 = 52 where A1 > 2"),
648+
PreparedParams.empty(),
649+
"update \"A\" set \"A2\" = ? where \"A1\" > ? ",
650+
List.of(Map.of(constantId(5), 52, constantId(9), 2)),
651+
null,
652+
-1,
653+
EnumSet.of(AstNormalizer.Result.QueryCachingFlags.IS_UPDATE_STATEMENT));
654+
}
655+
656+
@Test
657+
void parseInsertStatementSetsQueryFlagCorrectly() throws Exception {
658+
validate(List.of("insert into A values (42, 'foo')"),
659+
PreparedParams.empty(),
660+
"insert into \"A\" values ( ? , ? ) ",
661+
List.of(Map.of(constantId(5), 42, constantId(7), "foo")),
662+
null,
663+
-1,
664+
EnumSet.of(AstNormalizer.Result.QueryCachingFlags.IS_INSERT_STATEMENT));
665+
}
666+
667+
@Test
668+
void parseDeleteStatementSetsQueryFlagCorrectly() throws Exception {
669+
validate(List.of("delete from A where x = 42"),
670+
PreparedParams.empty(),
671+
"delete from \"A\" where \"X\" = ? ",
672+
List.of(Map.of(constantId(6), 42)),
673+
null,
674+
-1,
675+
EnumSet.of(AstNormalizer.Result.QueryCachingFlags.IS_DELETE_STATEMENT));
676+
}
677+
645678
@Test
646679
void queryHashWorksWithPreparedParameters() throws Exception {
647680
validate(List.of(

0 commit comments

Comments
 (0)