Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@
@Nonnull
private final Map<String, View> viewMap;
@Nonnull
private final Map<String, String> prepareStatements;
@Nonnull
private final Map<String, Index> indexes;
@Nonnull
private final Map<String, Index> universalIndexes;
Expand Down Expand Up @@ -118,6 +120,7 @@
Collections.unmodifiableList(orig.formerIndexes),
Collections.unmodifiableMap(orig.userDefinedFunctionMap),
Collections.unmodifiableMap(orig.viewMap),
Collections.unmodifiableMap(orig.prepareStatements),
orig.splitLongRecords,
orig.storeRecordVersions,
orig.version,
Expand All @@ -139,6 +142,7 @@
@Nonnull List<FormerIndex> formerIndexes,
@Nonnull Map<String, UserDefinedFunction> userDefinedFunctionMap,
@Nonnull Map<String, View> viewMap,
@Nonnull Map<String, String> prepareStatements,
boolean splitLongRecords,
boolean storeRecordVersions,
int version,
Expand All @@ -157,6 +161,7 @@
this.formerIndexes = formerIndexes;
this.userDefinedFunctionMap = userDefinedFunctionMap;
this.viewMap = viewMap;
this.prepareStatements = prepareStatements;

Check warning on line 164 in fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/RecordMetaData.java

View check run for this annotation

fdb.teamscale.io / Teamscale | Findings

fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/RecordMetaData.java#L164

Store a copy of `prepareStatements` https://fdb.teamscale.io/findings/details/foundationdb-fdb-record-layer?id=6412DB7F3AE7AD748DC13C61576EA58E&t=FORK_MR%2F4157%2Fsergei-pustovykh%2Fprepare-statements-metadata%3AHEAD
this.splitLongRecords = splitLongRecords;
this.storeRecordVersions = storeRecordVersions;
this.version = version;
Expand Down Expand Up @@ -704,6 +709,7 @@

builder.addAllUserDefinedFunctions(userDefinedFunctionMap.values().stream().map(UserDefinedFunction::toProto).collect(Collectors.toList()));
builder.addAllViews(viewMap.values().stream().map(View::toProto).collect(Collectors.toList()));
builder.putAllPrepareStatements(prepareStatements);
builder.setSplitLongRecords(splitLongRecords);
builder.setStoreRecordVersions(storeRecordVersions);
builder.setVersion(version);
Expand All @@ -728,6 +734,11 @@
return viewMap;
}

@Nonnull
public Map<String, String> getPrepareStatements() {
return prepareStatements;

Check warning on line 739 in fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/RecordMetaData.java

View check run for this annotation

fdb.teamscale.io / Teamscale | Findings

fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/RecordMetaData.java#L739

Return a copy of `prepareStatements` https://fdb.teamscale.io/findings/details/foundationdb-fdb-record-layer?id=1F2B4C7BCF1FD1F16D35F07373FDF66D&t=FORK_MR%2F4157%2Fsergei-pustovykh%2Fprepare-statements-metadata%3AHEAD
}

@Nonnull
public Type.Record getPlannerType(@Nonnull String recordTypeName) {
final RecordType recordType = getRecordType(recordTypeName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@
@Nonnull
private final Map<String, View> viewMap;
@Nonnull
private final Map<String, String> prepareStatements;
@Nonnull
private final Map<String, Index> indexes;
@Nonnull
private final Map<String, Index> universalIndexes;
Expand Down Expand Up @@ -152,6 +154,7 @@
syntheticRecordTypes = new HashMap<>();
userDefinedFunctionMap = new HashMap<>();
viewMap = new HashMap<>();
prepareStatements = new HashMap<>();
}

private void processSchemaOptions(boolean processExtensionOptions) {
Expand Down Expand Up @@ -238,6 +241,7 @@
final View view = View.fromProto(viewProto);
viewMap.put(view.getName(), view);
}
prepareStatements.putAll(metaDataProto.getPrepareStatementsMap());
if (metaDataProto.hasSplitLongRecords()) {
splitLongRecords = metaDataProto.getSplitLongRecords();
}
Expand Down Expand Up @@ -1215,6 +1219,15 @@
viewMap.put(view.getName(), view);
}

@Nonnull
public Map<String, String> getPrepareStatements() {
return prepareStatements;

Check warning on line 1224 in fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/RecordMetaDataBuilder.java

View check run for this annotation

fdb.teamscale.io / Teamscale | Findings

fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/RecordMetaDataBuilder.java#L1224

Return a copy of `prepareStatements` https://fdb.teamscale.io/findings/details/foundationdb-fdb-record-layer?id=5D32EE8989EBC00115E998C0F01462C6&t=FORK_MR%2F4157%2Fsergei-pustovykh%2Fprepare-statements-metadata%3AHEAD
}

public void addPrepareStatement(@Nonnull String name, @Nonnull String prepareStatement) {
prepareStatements.put(name, prepareStatement);
}

public boolean isSplitLongRecords() {
return splitLongRecords;
}
Expand Down Expand Up @@ -1456,7 +1469,7 @@
Map<Object, SyntheticRecordType<?>> recordTypeKeyToSyntheticRecordTypeMap = Maps.newHashMapWithExpectedSize(syntheticRecordTypes.size());
RecordMetaData metaData = new RecordMetaData(recordsDescriptor, getUnionDescriptor(), unionFields,
builtRecordTypes, builtSyntheticRecordTypes, recordTypeKeyToSyntheticRecordTypeMap,
indexes, universalIndexes, formerIndexes, userDefinedFunctionMap, viewMap,
indexes, universalIndexes, formerIndexes, userDefinedFunctionMap, viewMap, prepareStatements,
splitLongRecords, storeRecordVersions, version, subspaceKeyCounter, usesSubspaceKeyCounter, recordCountKey, localFileDescriptor != null);
for (RecordTypeBuilder recordTypeBuilder : recordTypes.values()) {
KeyExpression primaryKey = recordTypeBuilder.getPrimaryKey();
Expand Down
1 change: 1 addition & 0 deletions fdb-record-layer-core/src/main/proto/record_metadata.proto
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ message MetaData {
repeated UnnestedRecordType unnested_record_types = 13;
repeated PUserDefinedFunction user_defined_functions = 14;
repeated PView views = 15;
map<string, string> prepare_statements = 16;
extensions 1000 to 2000;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,7 @@ void validateMetaDataCoverage() {
assertEquals(Set.of(
"split_long_records", "version", "former_indexes", "record_count_key",
"store_record_versions", "dependencies", "subspace_key_counter", "uses_subspace_key_counter",
"prepare_statements",
// the below reference record types
"records", "indexes", "record_types", "joined_record_types", "unnested_record_types",
"user_defined_functions", "views"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import javax.annotation.Nonnull;
import java.util.BitSet;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

Expand Down Expand Up @@ -130,6 +131,14 @@ public interface SchemaTemplate extends Metadata {
@Nonnull
Collection<? extends InvokedRoutine> getTemporaryInvokedRoutines() throws RelationalException;

/**
* Returns the prepare statements defined in this schema template.
*
* @return A map of prepare statement names to their SQL strings.
*/
@Nonnull
Map<String, String> getPrepareStatements();

@Nonnull
String getTransactionBoundMetadataAsString() throws RelationalException;

Expand Down
1 change: 1 addition & 0 deletions fdb-relational-core/src/main/antlr/RelationalParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ utilityStatement
templateClause
:
CREATE ( structDefinition | tableDefinition | enumDefinition | indexDefinition | sqlInvokedFunction | viewDefinition )
| prepareStatement
;

createStatement
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
import javax.annotation.Nonnull;
import java.util.BitSet;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

Expand Down Expand Up @@ -139,6 +141,12 @@ public Collection<InvokedRoutine> getTemporaryInvokedRoutines() throws Relationa
throw new RelationalException("NoOpSchemaTemplate doesn't have temporary invoked routines!", ErrorCode.INVALID_PARAMETER);
}

@Nonnull
@Override
public Map<String, String> getPrepareStatements() {
return Collections.emptyMap();
}

@Nonnull
@Override
public String getTransactionBoundMetadataAsString() throws RelationalException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ public final class RecordLayerSchemaTemplate implements SchemaTemplate {
@Nonnull
private final Set<RecordLayerView> views;

@Nonnull
private final Map<String, String> prepareStatements;

private final int version;

private final boolean enableLongRows;
Expand Down Expand Up @@ -107,6 +110,7 @@ private RecordLayerSchemaTemplate(@Nonnull final String name,
@Nonnull final Set<RecordLayerTable> tables,
@Nonnull final Set<RecordLayerInvokedRoutine> invokedRoutines,
@Nonnull final Set<RecordLayerView> views,
@Nonnull final Map<String, String> prepareStatements,
int version,
boolean enableLongRows,
boolean storeRowVersions,
Expand All @@ -115,6 +119,7 @@ private RecordLayerSchemaTemplate(@Nonnull final String name,
this.tables = ImmutableSet.copyOf(tables);
this.invokedRoutines = ImmutableSet.copyOf(invokedRoutines);
this.views = ImmutableSet.copyOf(views);
this.prepareStatements = ImmutableMap.copyOf(prepareStatements);
this.version = version;
this.enableLongRows = enableLongRows;
this.storeRowVersions = storeRowVersions;
Expand All @@ -130,6 +135,7 @@ private RecordLayerSchemaTemplate(@Nonnull final String name,
@Nonnull final Set<RecordLayerTable> tables,
@Nonnull final Set<RecordLayerInvokedRoutine> invokedRoutines,
@Nonnull final Set<RecordLayerView> views,
@Nonnull final Map<String, String> prepareStatements,
int version,
boolean enableLongRows,
boolean storeRowVersions,
Expand All @@ -140,6 +146,7 @@ private RecordLayerSchemaTemplate(@Nonnull final String name,
this.tables = ImmutableSet.copyOf(tables);
this.invokedRoutines = ImmutableSet.copyOf(invokedRoutines);
this.views = ImmutableSet.copyOf(views);
this.prepareStatements = ImmutableMap.copyOf(prepareStatements);
this.enableLongRows = enableLongRows;
this.storeRowVersions = storeRowVersions;
this.intermingleTables = intermingleTables;
Expand Down Expand Up @@ -338,6 +345,12 @@ public Set<RecordLayerView> getViews() {
return views;
}

@Nonnull
@Override
public Map<String, String> getPrepareStatements() {
return prepareStatements;
}

@Nonnull
@Override
public Optional<? extends View> findViewByName(@Nonnull final String viewName) {
Expand Down Expand Up @@ -414,6 +427,9 @@ public static final class Builder {
@Nonnull
private final Map<String, RecordLayerView> views;

@Nonnull
private final Map<String, String> prepareStatements;


private RecordMetaData cachedMetadata;

Expand All @@ -422,6 +438,7 @@ private Builder() {
auxiliaryTypes = new LinkedHashMap<>();
invokedRoutines = new LinkedHashMap<>();
views = new LinkedHashMap<>();
prepareStatements = new LinkedHashMap<>();
// enable long rows is TRUE by default
enableLongRows = true;
}
Expand Down Expand Up @@ -540,6 +557,18 @@ public Builder addViews(@Nonnull final Collection<RecordLayerView> views) {
return this;
}

@Nonnull
public Builder addPrepareStatement(@Nonnull final String name, @Nonnull final String prepareStatement) {
prepareStatements.put(name, prepareStatement);
return this;
}

@Nonnull
public Builder addPrepareStatements(@Nonnull final Map<String, String> prepareStatements) {
this.prepareStatements.putAll(prepareStatements);
return this;
}

/**
* Adds an auxiliary type, an auxiliary type is a type that is merely created, so it can be referenced later on
* in a table definition. Any {@link DataType.Named} data type can be added as an auxiliary type such as {@code enum}s
Expand Down Expand Up @@ -632,10 +661,10 @@ public RecordLayerSchemaTemplate build() {

if (cachedMetadata != null) {
return new RecordLayerSchemaTemplate(name, new LinkedHashSet<>(tables.values()),
new LinkedHashSet<>(invokedRoutines.values()), new LinkedHashSet<>(views.values()), version, enableLongRows, storeRowVersions, intermingleTables, cachedMetadata);
new LinkedHashSet<>(invokedRoutines.values()), new LinkedHashSet<>(views.values()), prepareStatements, version, enableLongRows, storeRowVersions, intermingleTables, cachedMetadata);
} else {
return new RecordLayerSchemaTemplate(name, new LinkedHashSet<>(tables.values()),
new LinkedHashSet<>(invokedRoutines.values()), new LinkedHashSet<>(views.values()), version, enableLongRows, storeRowVersions, intermingleTables);
new LinkedHashSet<>(invokedRoutines.values()), new LinkedHashSet<>(views.values()), prepareStatements, version, enableLongRows, storeRowVersions, intermingleTables);
}
}

Expand Down Expand Up @@ -763,6 +792,7 @@ public Builder toBuilder() {
.setIntermingleTables(intermingleTables)
.addTables(getTables())
.addInvokedRoutines(getInvokedRoutines())
.addViews(getViews());
.addViews(getViews())
.addPrepareStatements(getPrepareStatements());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ private static RecordLayerSchemaTemplate.Builder deserializeRecordMetaData(@Nonn
schemaTemplateBuilder.addView(generateViewBuilder(metadataProvider, view.getKey(), view.getValue().getDefinition()).build());
}
}
schemaTemplateBuilder.addPrepareStatements(recordMetaData.getPrepareStatements());
schemaTemplateBuilder.setCachedMetadata(recordMetaData);
return schemaTemplateBuilder;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,13 @@ public void visit(@Nonnull final View view) {
@Override
public void visit(@Nonnull SchemaTemplate schemaTemplate) {
Assert.thatUnchecked(schemaTemplate instanceof RecordLayerSchemaTemplate);
final var recLayerSchemaTemplate = (RecordLayerSchemaTemplate) schemaTemplate;
getBuilder().setSplitLongRecords(schemaTemplate.isEnableLongRows());
getBuilder().setStoreRecordVersions(schemaTemplate.isStoreRowVersions());
getBuilder().setVersion(schemaTemplate.getVersion());
for (final var entry : recLayerSchemaTemplate.getPrepareStatements().entrySet()) {
getBuilder().addPrepareStatement(entry.getKey(), entry.getValue());
}
}

@Nonnull
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import com.apple.foundationdb.record.RecordMetaData;
import com.apple.foundationdb.record.RecordStoreState;
import com.apple.foundationdb.record.logging.KeyValueLogMessage;
import com.apple.foundationdb.record.logging.LogMessageKeys;
import com.apple.foundationdb.record.metadata.MetaDataException;
import com.apple.foundationdb.record.provider.foundationdb.FDBRecordStoreBase;
import com.apple.foundationdb.record.provider.foundationdb.IndexMatchCandidateRegistry;
Expand Down Expand Up @@ -140,6 +141,35 @@ public Plan<?> getPlan(@Nonnull final String query) throws RelationalException {
return plan;
}

/**
* Pre-generates and caches plans for the prepare statements defined in the schema template.
* This method is idempotent per template name and version — subsequent calls for the same
* template are no-ops.
*/
public void prepareStatements() {
if (cache.isEmpty()) {
return;
}
final var schemaTemplate = planContext.getSchemaTemplate();
if (schemaTemplate.getPrepareStatements().isEmpty()) {
return;
}
final var templateKey = schemaTemplate.getName() + ":" + schemaTemplate.getVersion();
if (cache.get().isPrepared(templateKey)) {
return;
}
for (final var entry : schemaTemplate.getPrepareStatements().entrySet()) {
try {
getPlan(entry.getValue());
} catch (RelationalException e) {
if (logger.isErrorEnabled()) {
logger.error(KeyValueLogMessage.of("prepare statement", LogMessageKeys.QUERY, entry.getValue()), e);
}
}
}
cache.get().markPrepared(templateKey);
}

private boolean isCaseSensitive() {
return options.getOption(Options.Name.CASE_SENSITIVE_IDENTIFIERS);
}
Expand Down Expand Up @@ -480,7 +510,9 @@ public static PlanGenerator create(@Nonnull final Optional<RelationalPlanCache>
@Nonnull final Options options) throws RelationalException {
final var planner = new CascadesPlanner(metaData, recordStoreState, matchCandidateRegistry);
planner.setConfiguration(planContext.getRecordQueryPlannerConfiguration());
return new PlanGenerator(cache, planContext, planner, options);
final var planGenerator = new PlanGenerator(cache, planContext, planner, options);
planGenerator.prepareStatements();
return planGenerator;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;

Expand All @@ -45,6 +47,9 @@ public final class RelationalPlanCache extends MultiStageCache<String, QueryCach
@Nonnull
private static final TimeUnit DEFAULT_TERTIARY_TTL_TIME_UNIT = TimeUnit.MILLISECONDS;

@Nonnull
private final Set<String> preparedTemplates = ConcurrentHashMap.newKeySet();

private RelationalPlanCache(int size,
int secondarySize,
int tertiarySize,
Expand Down Expand Up @@ -102,4 +107,12 @@ public static RelationalPlanCache buildWithDefaults() {
return newRelationalCacheBuilder().build();
}

public boolean isPrepared(@Nonnull String templateKey) {
return preparedTemplates.contains(templateKey);
}

public void markPrepared(@Nonnull String templateKey) {
preparedTemplates.add(templateKey);
}

}
Loading
Loading