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 @@ -463,6 +463,9 @@ public boolean copy(@Nonnull Descriptors.Descriptor recordDescriptor, @Nonnull M
return !fieldDescriptor.isRequired();
}
switch (fieldDescriptor.getType()) {
case INT32:
value = ((Number)value).intValue();
break;
case MESSAGE:
value = TupleFieldsHelper.toProto(value, fieldDescriptor.getMessageType());
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ public abstract class EmbeddedRelationalBenchmark {
"CREATE TABLE \"RestaurantRecord\" (\"rest_no\" bigint, \"name\" string, \"location\" \"Location\", \"reviews\" \"RestaurantReview\" ARRAY, \"tags\" \"RestaurantTag\" ARRAY, \"customer\" string ARRAY, PRIMARY KEY(\"rest_no\")) " +
"CREATE TABLE \"RestaurantReviewer\" (\"id\" bigint, \"name\" string, \"email\" string, \"stats\" \"ReviewerStats\", PRIMARY KEY(\"id\")) " +

"CREATE INDEX \"record_name_idx\" as select \"name\" from \"RestaurantRecord\" " +
"CREATE INDEX \"reviewer_name_idx\" as select \"name\" from \"RestaurantReviewer\" ";
"CREATE INDEX \"record_name_idx\" ON \"RestaurantRecord\"(\"name\") " +
"CREATE INDEX \"reviewer_name_idx\" ON \"RestaurantReviewer\"(\"name\") ";

static final String restaurantRecordTable = "RestaurantRecord";

Expand Down
1 change: 1 addition & 0 deletions fdb-relational-core/src/main/antlr/RelationalLexer.g4
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ USAGE: 'USAGE';
USE: 'USE';
USING: 'USING';
VALUES: 'VALUES';
VECTOR: 'VECTOR';
WHEN: 'WHEN';
WHERE: 'WHERE';
WHILE: 'WHILE';
Expand Down
29 changes: 26 additions & 3 deletions fdb-relational-core/src/main/antlr/RelationalParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,24 @@ enumDefinition
;

indexDefinition
: (UNIQUE)? INDEX indexName=uid AS queryTerm indexAttributes?
: (UNIQUE)? INDEX indexName=uid AS queryTerm indexAttributes? #indexAsSelectDefinition
| (UNIQUE)? INDEX indexName=uid ON tableName indexColumnList includeClause? partitionClause? indexAttributes? #indexOnSourceDefinition
;

indexColumnList
: '(' indexColumnSpec (',' indexColumnSpec)* ')'
;

indexColumnSpec
: columnName=uid orderClause?
;

includeClause
: INCLUDE '(' uidList ')'
;

indexType
: UNIQUE | VECTOR
;

indexAttributes
Expand Down Expand Up @@ -406,7 +423,12 @@ orderByClause
;

orderByExpression
: expression order=(ASC | DESC)? (NULLS nulls=(FIRST | LAST))?
: expression orderClause?
;

orderClause
: order=(ASC | DESC) (NULLS nulls=(FIRST | LAST))?
| NULLS nulls=(FIRST | LAST)
;

tableSources // done
Expand Down Expand Up @@ -1107,10 +1129,11 @@ frameRange
| expression (PRECEDING | FOLLOWING)
;

*/

partitionClause
: PARTITION BY expression (',' expression)*
;
*/

scalarFunctionName
: functionNameBase
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,10 +215,15 @@ private int countUpdates(@Nonnull ResultSet resultSet) throws SQLException {
}
return count;
} catch (SQLException | RuntimeException ex) {
if (conn.canCommit()) {
conn.rollbackInternal();
SQLException finalException = ExceptionUtil.toRelationalException(ex).toSqlException();
try {
if (conn.canCommit()) {
conn.rollbackInternal();
}
} catch (SQLException | RuntimeException rollbackError) {
finalException.addSuppressed(rollbackError);
}
throw ExceptionUtil.toRelationalException(ex).toSqlException();
throw finalException;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import org.antlr.v4.runtime.tree.ParseTree;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Base64;
import java.util.Locale;
import java.util.function.Supplier;
Expand Down Expand Up @@ -159,13 +160,18 @@ public static byte[] parseBytes(String text) {
}
}

public static boolean isDescending(@Nonnull RelationalParser.OrderByExpressionContext orderByExpressionContext) {
return (orderByExpressionContext.ASC() == null) && (orderByExpressionContext.DESC() != null);
public static boolean isNullsLast(@Nullable RelationalParser.OrderClauseContext orderClause, boolean isDescending) {
if (orderClause == null || orderClause.nulls == null) {
return isDescending; // Default behavior: ASC NULLS FIRST, DESC NULLS LAST
}
return orderClause.LAST() != null;
}

public static boolean isNullsLast(@Nonnull RelationalParser.OrderByExpressionContext orderByExpressionContext, boolean isDescending) {
return orderByExpressionContext.nulls == null ? isDescending :
(orderByExpressionContext.FIRST() == null) && (orderByExpressionContext.LAST() != null);
public static boolean isDescending(@Nullable RelationalParser.OrderClauseContext orderClause) {
if (orderClause == null) {
return false; // Default is ASC
}
return orderClause.DESC() != null;
}

public static class ParseTreeLikeAdapter implements TreeLike<ParseTreeLikeAdapter> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -403,8 +403,32 @@ public DataType.Named visitEnumDefinition(@Nonnull RelationalParser.EnumDefiniti

@Nonnull
@Override
public RecordLayerIndex visitIndexDefinition(@Nonnull RelationalParser.IndexDefinitionContext ctx) {
return ddlVisitor.visitIndexDefinition(ctx);
public RecordLayerIndex visitIndexAsSelectDefinition(@Nonnull RelationalParser.IndexAsSelectDefinitionContext ctx) {
return ddlVisitor.visitIndexAsSelectDefinition(ctx);
}

@Nonnull
@Override
public RecordLayerIndex visitIndexOnSourceDefinition(@Nonnull RelationalParser.IndexOnSourceDefinitionContext ctx) {
return ddlVisitor.visitIndexOnSourceDefinition(ctx);
}

@Nonnull
@Override
public Object visitIndexColumnList(@Nonnull RelationalParser.IndexColumnListContext ctx) {
return ddlVisitor.visitIndexColumnList(ctx);
}

@Nonnull
@Override
public Object visitIndexColumnSpec(@Nonnull RelationalParser.IndexColumnSpecContext ctx) {
return ddlVisitor.visitIndexColumnSpec(ctx);
}

@Nonnull
@Override
public Object visitIncludeClause(@Nonnull RelationalParser.IncludeClauseContext ctx) {
return ddlVisitor.visitIncludeClause(ctx);
}

@Override
Expand Down Expand Up @@ -1696,4 +1720,9 @@ public DdlQueryFactory getDdlQueryFactory() {
public URI getDbUri() {
return dbUri;
}

@Override
public Object visitOrderClause(@Nonnull RelationalParser.OrderClauseContext ctx) {
return visitChildren(ctx);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
package com.apple.foundationdb.relational.recordlayer.query.visitors;

import com.apple.foundationdb.annotation.API;
import com.apple.foundationdb.record.metadata.Key;
import com.apple.foundationdb.record.metadata.IndexTypes;
import com.apple.foundationdb.record.metadata.expressions.KeyExpression;
import com.apple.foundationdb.record.query.plan.cascades.expressions.LogicalSortExpression;
import com.apple.foundationdb.record.query.plan.cascades.values.PromoteValue;
import com.apple.foundationdb.record.query.plan.cascades.values.ThrowsValue;
Expand All @@ -30,7 +33,6 @@
import com.apple.foundationdb.relational.api.metadata.DataType;
import com.apple.foundationdb.relational.api.metadata.InvokedRoutine;
import com.apple.foundationdb.relational.generated.RelationalParser;
import com.apple.foundationdb.relational.recordlayer.metadata.DataTypeUtils;
import com.apple.foundationdb.relational.recordlayer.metadata.RecordLayerColumn;
import com.apple.foundationdb.relational.recordlayer.metadata.RecordLayerIndex;
import com.apple.foundationdb.relational.recordlayer.metadata.RecordLayerInvokedRoutine;
Expand All @@ -42,11 +44,13 @@
import com.apple.foundationdb.relational.recordlayer.query.Identifier;
import com.apple.foundationdb.relational.recordlayer.query.IndexGenerator;
import com.apple.foundationdb.relational.recordlayer.query.LogicalOperator;
import com.apple.foundationdb.relational.recordlayer.query.ParseHelpers;
import com.apple.foundationdb.relational.recordlayer.query.PreparedParams;
import com.apple.foundationdb.relational.recordlayer.query.ProceduralPlan;
import com.apple.foundationdb.relational.recordlayer.query.QueryParser;
import com.apple.foundationdb.relational.recordlayer.query.SemanticAnalyzer;
import com.apple.foundationdb.relational.recordlayer.query.functions.CompiledSqlFunction;
import com.apple.foundationdb.relational.recordlayer.metadata.DataTypeUtils;
import com.apple.foundationdb.relational.util.Assert;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
Expand Down Expand Up @@ -184,7 +188,7 @@

@Nonnull
@Override
public RecordLayerIndex visitIndexDefinition(@Nonnull RelationalParser.IndexDefinitionContext ctx) {
public RecordLayerIndex visitIndexAsSelectDefinition(@Nonnull RelationalParser.IndexAsSelectDefinitionContext ctx) {
final var indexId = visitUid(ctx.indexName);

final var ddlCatalog = metadataBuilder.build();
Expand All @@ -201,6 +205,121 @@
return generator.generate(indexId.getName(), isUnique, table.getType(), containsNullableArray);
}

@Nonnull
@Override
public RecordLayerIndex visitIndexOnSourceDefinition(@Nonnull RelationalParser.IndexOnSourceDefinitionContext ctx) {
final var indexId = visitUid(ctx.indexName);
final var tableName = visitUid(ctx.tableName().fullId().uid(0));
final var isUnique = ctx.UNIQUE() != null;

// Build KeyExpression directly from DDL syntax - no SQL generation needed
final var keyExpression = buildKeyExpressionFromDdl(ctx);

return RecordLayerIndex.newBuilder()
.setName(indexId.getName())
.setTableName(tableName.getName())
.setIndexType(IndexTypes.VALUE)
.setKeyExpression(keyExpression)
.setUnique(isUnique)
.build();
}

private KeyExpression buildKeyExpressionFromDdl(@Nonnull RelationalParser.IndexOnSourceDefinitionContext ctx) {
final var columnSpecs = ctx.indexColumnList().indexColumnSpec();
final List<KeyExpression> indexedExpressions = new ArrayList<>();
final List<String> includeColumns = new ArrayList<>();

// Extract indexed columns with ASC/DESC and NULLS FIRST/LAST ordering
for (final var spec : columnSpecs) {
final var columnName = visitUid(spec.columnName).getName();
final var fieldExpression = Key.Expressions.field(columnName);

// Check if orderClause is specified
if (spec.orderClause() != null) {
final boolean isDesc = ParseHelpers.isDescending(spec.orderClause());
final boolean nullsLast = ParseHelpers.isNullsLast(spec.orderClause(), isDesc);

if (isDesc) {
if (nullsLast) {
indexedExpressions.add(Key.Expressions.function("order_desc_nulls_last", fieldExpression));
} else {
indexedExpressions.add(Key.Expressions.function("order_desc_nulls_first", fieldExpression));
}
} else {
if (nullsLast) {
indexedExpressions.add(Key.Expressions.function("order_asc_nulls_last", fieldExpression));
} else {
indexedExpressions.add(fieldExpression);
}
}
} else {
// No ordering specified - ASC NULLS FIRST (default)
indexedExpressions.add(fieldExpression);
}
}

// Extract INCLUDE columns
if (ctx.includeClause() != null) {
final var includeColumnCtxs = ctx.includeClause().uidList().uid();
for (final var includeColumnCtx : includeColumnCtxs) {
final var columnName = visitUid(includeColumnCtx).getName();
includeColumns.add(columnName);
}
}

// Combine indexed expressions + include columns for KeyExpression
final List<KeyExpression> allExpressions = new ArrayList<>(indexedExpressions);
for (final var includeColumn : includeColumns) {
allExpressions.add(Key.Expressions.field(includeColumn));
}

if (allExpressions.size() == 1) {
return allExpressions.get(0);
} else {
if (!includeColumns.isEmpty()) {
return Key.Expressions.keyWithValue(Key.Expressions.concat(allExpressions), indexedExpressions.size());
} else {
return Key.Expressions.concat(allExpressions);
}
}
}

// public RecordLayerIndex visitDeclarativeIndexDefinition(@Nonnull RelationalParser.DeclarativeIndexDefinitionContext ctx) {
// final var indexId = visitUid(ctx.indexHeader().indexName);
// final var sourceId = visitUid(ctx.indexHeader().source);
// final var indexType = ctx.indexType() == null ? Optional.empty() : Optional.of(ctx.indexType().getText());
//
// final var ddlCatalog = metadataBuilder.build();
// // parse the index SQL query using the newly constructed metadata.
// getDelegate().replaceSchemaTemplate(ddlCatalog);
//
//
// LogicalOperator operator;
// if (getDelegate().getSemanticAnalyzer().viewExists(sourceId)) {
// operator = getDelegate().getSemanticAnalyzer().resolveView(sourceId);
// } else if (getDelegate().getSemanticAnalyzer().tableExists(sourceId)) {
// return LogicalOperator.generateAccess(identifier, alias, requestedIndexes, semanticAnalyzer);
// } else if (getDelegate().getSemanticAnalyzer().resolveView() !+ ) {
//
// }
//
// getDelegate().pushPlanFragment();
// final var operator = LogicalOperator.generateAccess(sourceId, Optional.empty(), Set.of(), getDelegate().getSemanticAnalyzer(),
// getDelegate().getCurrentPlanFragment(), getDelegate().getLogicalOperatorCatalog());
//
//
// final var result = LogicalOperator.generateSelect(getDelegate().getSemanticAnalyzer().expandStar(), getDelegate().getLogicalOperators(), where, orderBys,
// Optional.empty(), outerCorrelations, getDelegate().isTopLevel(), getDelegate().isForDdl());
//
//
// final var plan = operator.getQuantifier().getRangesOver().get();
//
// getDelegate().popPlanFragment();
//
//
// return null;
// }

Check warning on line 321 in fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/visitors/DdlVisitor.java

View check run for this annotation

fdb.teamscale.io / Teamscale | Findings

fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/visitors/DdlVisitor.java#L287-L321

[New] Commented Out Code https://fdb.teamscale.io/findings/details/foundationdb-fdb-record-layer?t=FORK_MR%2F3701%2Fg31pranjal%2Fnew_index_definition%3AHEAD&id=45255E9692DE35DDB63F1ED136825123

@Nonnull
@Override
public DataType.Named visitEnumDefinition(@Nonnull RelationalParser.EnumDefinitionContext ctx) {
Expand Down Expand Up @@ -257,7 +376,7 @@
}
structClauses.build().stream().map(this::visitStructDefinition).map(RecordLayerTable::getDatatype).forEach(metadataBuilder::addAuxiliaryType);
tableClauses.build().stream().map(this::visitTableDefinition).forEach(metadataBuilder::addTable);
final var indexes = indexClauses.build().stream().map(this::visitIndexDefinition).collect(ImmutableList.toImmutableList());
final var indexes = indexClauses.build().stream().map(clause -> Assert.castUnchecked(visit(clause), RecordLayerIndex.class)).collect(ImmutableList.toImmutableList());
// TODO: this is currently relying on the lexical order of the functions and views to resolve dependencies which is limited.
// https://github.com/FoundationDB/fdb-record-layer/issues/3493
functionClauses.build().forEach(functionClause -> {
Expand Down
Loading
Loading