createRelDataTypeField(RelDataTypeField field) {
+ return getOutputType(field.getType(), NamePath.of(field.getName()), extendedScalarTypes)
+ .filter(type -> isValidGraphQLName(field.getName()))
+ .filter(type -> isVisible(field))
+ .map(type -> GraphQLFieldDefinition.newFieldDefinition()
+ .name(field.getName())
+ .type((GraphQLOutputType) wrapNullable(type, field.getType()))
+ .build()
+ );
+ }
+
+ /**
+ * For a relationship table function sur as this: {@code Customer.orders := SELECT * FROM Orders o WHERE
+ * this.customerid = o.customerid; }
Create a type reference for the orders field in the Customer table.
+ */
+ private Optional createRelationshipField(SqrlTableFunction relationship) {
+ String fieldName = relationship.getFullPath().getLast().getDisplay();
+ if (!isValidGraphQLName(fieldName) || isHiddenString(fieldName)) {
+ return Optional.empty();
+ }
+
+ // reference the type that will be defined when the table function relationship is processed
+ GraphQLFieldDefinition field = GraphQLFieldDefinition.newFieldDefinition()
+ .name(fieldName)
+ .type((GraphQLOutputType) wrapMultiplicity(createTypeReference(relationship), relationship.getMultiplicity()))
+ .arguments(createArguments(relationship))
+ .build();
+
+ return Optional.of(field);
+ }
+
+ private List createArguments(SqrlTableFunction tableFunction) {
+ if (tableFunction.getMultiplicity() != Multiplicity.MANY) {
+ return List.of();
+ }
+
+ List parameters = tableFunction.getParameters().stream()
+ .filter(parameter->!((SqrlFunctionParameter)parameter).isParentField())
+ .collect(Collectors.toList());
+
+ final List parametersArguments = parameters.stream()
+ .filter(p -> getInputType(p.getType(null), NamePath.of(p.getName()), extendedScalarTypes).isPresent())
+ .map(parameter -> GraphQLArgument.newArgument()
+ .name(parameter.getName())
+ .type(nonNull(getInputType(parameter.getType(null), NamePath.of(parameter.getName()), extendedScalarTypes).get()))
+ .build()).collect(Collectors.toList());
+ List limitAndOffsetArguments = List.of();
+ if(tableFunction.getVisibility().getAccess() != AccessModifier.SUBSCRIPTION) {
+ limitAndOffsetArguments = generateLimitAndOffsetArguments();
+ }
+ return ListUtils.union(parametersArguments, limitAndOffsetArguments);
+ }
+
+ private List generateLimitAndOffsetArguments() {
+ GraphQLArgument limit = GraphQLArgument.newArgument()
+ .name(LIMIT)
+ .type(Scalars.GraphQLInt)
+ .defaultValueLiteral(IntValue.of(10))
+ .build();
+
+ GraphQLArgument offset = GraphQLArgument.newArgument()
+ .name(OFFSET)
+ .type(Scalars.GraphQLInt)
+ .defaultValueLiteral(IntValue.of(0))
+ .build();
+ return List.of(limit, offset);
+ }
+
+
+ private boolean isVisible(RelDataTypeField f) {
+ return !f.getName().startsWith(HIDDEN_PREFIX);
+ }
+
+ private GraphQLOutputType createTypeReference(SqrlTableFunction tableFunction) {
+ String typeName =
+ tableFunction.getFunctionAnalysis().getOptionalBaseTable().isPresent()
+ ? tableFunction.getBaseTable().getName()
+ : uniquifyNameForPath(tableFunction.getFullPath());
+ return new GraphQLTypeReference(typeName);
+ }
+
+
+ // TODO no more needed ?
+ public void cleanInvalidTypes() {
+ // Ensure every field points to a valid type
+ boolean found;
+ int attempts = 10;
+ do {
+ found = false;
+ Iterator iterator = objectTypes.iterator();
+ List replacedType = new ArrayList<>();
+ while (iterator.hasNext()) {
+ GraphQLObjectType objectType = iterator.next();
+ List invalidFields = new ArrayList<>();
+
+ for (GraphQLFieldDefinition field : objectType.getFields()) {
+ if (!isValidType(field.getType())) {
+ invalidFields.add(field);
+ }
+ }
+
+ // Refactor to remove invalid fields
+ List fields = new ArrayList<>(objectType.getFields());
+ boolean fieldsRemoved = fields.removeAll(invalidFields);
+
+ // After removing invalid fields, if an object has no fields, it should be removed
+ if (fields.isEmpty()) {
+ iterator.remove();
+ found = true;
+ } else if (fieldsRemoved) {
+ GraphQLObjectType newObjectType = GraphQLObjectType.newObject(objectType).clearFields()
+ .fields(fields).build();
+ replacedType.add(newObjectType);
+ iterator.remove();
+ found = true;
+ }
+ }
+
+ //Add new types back
+ objectTypes.addAll(replacedType);
+
+ found |= queryFields.removeIf(field -> !isValidType(field.getType()));
+
+ // Ensure each object has at least one field
+ found |= objectTypes.removeIf(objectType -> objectType.getFields().isEmpty());
+ } while (found && --attempts != 0);
+
+ if (found) {
+ throw new RuntimeException("Schema too complexity high, could not be reduced");
+ }
+ }
+
+ boolean isValidType(GraphQLType type) {
+ type = unbox(type);
+ // You can expand this logic depending on the intricacies of type validation
+ if (type instanceof GraphQLTypeReference) {
+ GraphQLTypeReference typeReference = (GraphQLTypeReference) type;
+ for (GraphQLObjectType objectType : this.objectTypes) {
+ if (typeReference.getName().equalsIgnoreCase(objectType.getName())) {
+ return true;
+ }
+ }
+ }
+
+ return isBaseGraphQLType(type);
+ }
+
+ private GraphQLType unbox(GraphQLType type) {
+ if (type instanceof GraphQLNonNull) {
+ return unbox(((GraphQLNonNull) type).getWrappedType());
+ } else if (type instanceof GraphQLList) {
+ return unbox(((GraphQLList) type).getWrappedType());
+ }
+ return type;
+ }
+
+ boolean isBaseGraphQLType(GraphQLType type) {
+ return type instanceof GraphQLScalarType;
+ }
+}
diff --git a/sqrl-planner/src/main/java/com/datasqrl/v2/graphql/GraphqlSchemaUtil2.java b/sqrl-planner/src/main/java/com/datasqrl/v2/graphql/GraphqlSchemaUtil2.java
new file mode 100644
index 0000000000..3ce14da450
--- /dev/null
+++ b/sqrl-planner/src/main/java/com/datasqrl/v2/graphql/GraphqlSchemaUtil2.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) 2021, DataSQRL. All rights reserved. Use is subject to license terms.
+ */
+package com.datasqrl.v2.graphql;
+
+import static com.datasqrl.canonicalizer.Name.HIDDEN_PREFIX;
+import static com.datasqrl.canonicalizer.Name.isSystemHidden;
+
+import com.datasqrl.canonicalizer.Name;
+import com.datasqrl.canonicalizer.NamePath;
+import com.datasqrl.graphql.server.CustomScalars;
+import com.datasqrl.json.FlinkJsonType;
+import com.datasqrl.schema.Multiplicity;
+import graphql.Scalars;
+import graphql.schema.GraphQLFieldDefinition;
+import graphql.schema.GraphQLInputObjectField;
+import graphql.schema.GraphQLInputObjectType;
+import graphql.schema.GraphQLInputType;
+import graphql.schema.GraphQLList;
+import graphql.schema.GraphQLNonNull;
+import graphql.schema.GraphQLObjectType;
+import graphql.schema.GraphQLOutputType;
+import graphql.schema.GraphQLType;
+import java.util.Optional;
+import java.util.regex.Pattern;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeField;
+import org.apache.flink.table.planner.plan.schema.RawRelDataType;
+
+@Slf4j
+public class GraphqlSchemaUtil2 {
+
+ public static GraphQLType wrapNullable(GraphQLType gqlType, RelDataType type) {
+ if (!type.isNullable()) {
+ return GraphQLNonNull.nonNull(gqlType);
+ }
+ return gqlType;
+ }
+
+ public static GraphQLType wrapMultiplicity(GraphQLType type, Multiplicity multiplicity) {
+ switch (multiplicity) {
+ case ZERO_ONE:
+ return type;
+ case ONE:
+ return GraphQLNonNull.nonNull(type);
+ case MANY:
+ default:
+ return GraphQLList.list(GraphQLNonNull.nonNull(type));
+ }
+ }
+
+ public static boolean isValidGraphQLName(String name) {
+ return !isSystemHidden(name) && Pattern.matches("[_A-Za-z][_0-9A-Za-z]*", name);
+ }
+
+ public static Optional getInputType(RelDataType type, NamePath namePath, boolean extendedScalarTypes) {
+ return getInOutType(type, namePath, extendedScalarTypes)
+ .map(f->(GraphQLInputType)f);
+ }
+
+ public static Optional getOutputType(RelDataType type, NamePath namePath, boolean extendedScalarTypes) {
+ return getInOutType(type, namePath, extendedScalarTypes)
+ .map(f->(GraphQLOutputType)f);
+ }
+
+ public static Optional getInOutType(RelDataType type, NamePath namePath, boolean extendedScalarTypes) {
+ if (type.getSqlTypeName() == null) {
+ return Optional.empty();
+ }
+
+ switch (type.getSqlTypeName()) {
+ case OTHER:
+ if (type instanceof RawRelDataType) {
+ RawRelDataType rawRelDataType = (RawRelDataType) type;
+ Class> originatingClass = rawRelDataType.getRawType().getOriginatingClass();
+ if (originatingClass.isAssignableFrom(FlinkJsonType.class)) {
+ return Optional.of(CustomScalars.JSON);
+ }
+ }
+
+ return Optional.empty();
+ case BOOLEAN:
+ return Optional.of(Scalars.GraphQLBoolean);
+ case TINYINT:
+ case SMALLINT:
+ case INTEGER:
+ return Optional.of(Scalars.GraphQLInt);
+ case BIGINT:
+ if (extendedScalarTypes) {
+ return Optional.of(CustomScalars.GRAPHQL_BIGINTEGER);
+ }
+ case DECIMAL:
+ case FLOAT:
+ case REAL:
+ case DOUBLE:
+ return Optional.of(Scalars.GraphQLFloat);
+ case DATE:
+ return Optional.of(CustomScalars.DATE);
+ case TIME:
+ return Optional.of(CustomScalars.TIME);
+ case TIME_WITH_LOCAL_TIME_ZONE:
+ case TIMESTAMP:
+ case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
+ return Optional.of(CustomScalars.DATETIME);
+ case INTERVAL_YEAR:
+ case INTERVAL_YEAR_MONTH:
+ case INTERVAL_MONTH:
+ case INTERVAL_DAY:
+ case INTERVAL_DAY_HOUR:
+ case INTERVAL_DAY_MINUTE:
+ case INTERVAL_DAY_SECOND:
+ case INTERVAL_HOUR:
+ case INTERVAL_HOUR_MINUTE:
+ case INTERVAL_HOUR_SECOND:
+ case INTERVAL_MINUTE:
+ case INTERVAL_MINUTE_SECOND:
+ case INTERVAL_SECOND:
+ case CHAR:
+ case VARCHAR:
+ return Optional.of(Scalars.GraphQLString);
+ // arity many, create a GraphQLList of the component type
+ case ARRAY:
+ case MULTISET:
+ return getOutputType(type.getComponentType(), namePath, extendedScalarTypes).map(GraphQLList::list);
+ // nested type, arity 1
+ case STRUCTURED:
+ case ROW:
+ return createGraphQLOutputStructuredType(type, namePath, extendedScalarTypes);
+ case MAP:
+ return Optional.of(CustomScalars.JSON);
+ case BINARY:
+ case VARBINARY:
+ case NULL:
+ case ANY:
+ case SYMBOL:
+ case DISTINCT:
+ case CURSOR:
+ case COLUMN_LIST:
+ case DYNAMIC_STAR:
+ case GEOMETRY:
+ case SARG:
+ default:
+ return Optional.empty();
+ }
+ }
+
+
+ //TODO will be used for mutations. Do not remove, inline when coding mutations
+ public static Optional createOutputTypeForRelDataType(RelDataType type, NamePath namePath, boolean extendedScalarTypes) {
+ return getOutputType(type, namePath, extendedScalarTypes).map(t -> (GraphQLOutputType) wrapNullable(t, type));
+ }
+
+ private static Optional getGraphQLInputType(RelDataType type, NamePath namePath, boolean extendedScalarTypes) {
+ switch (type.getSqlTypeName()) {
+ case BOOLEAN:
+ return Optional.of(Scalars.GraphQLBoolean);
+ case INTEGER:
+ case SMALLINT:
+ case TINYINT:
+ return Optional.of(Scalars.GraphQLInt);
+ case BIGINT:
+ if (extendedScalarTypes) {
+ return Optional.of(CustomScalars.GRAPHQL_BIGINTEGER);
+ }
+ case FLOAT:
+ case REAL:
+ case DOUBLE:
+ case DECIMAL:
+ return Optional.of(Scalars.GraphQLFloat);
+ case CHAR:
+ case VARCHAR:
+ return Optional.of(Scalars.GraphQLString);
+ case DATE:
+ return Optional.of(CustomScalars.DATE);
+ case TIME:
+ return Optional.of(CustomScalars.TIME);
+ case TIMESTAMP:
+ return Optional.of(CustomScalars.DATETIME);
+ case BINARY:
+ case VARBINARY:
+ return Optional.of(Scalars.GraphQLString); // Typically handled as Base64 encoded strings
+ case ARRAY:
+ return type.getComponentType() != null // traverse inner type
+ ? getGraphQLInputType(type.getComponentType(), namePath, extendedScalarTypes).map(GraphQLList::list)
+ : Optional.empty();
+ case ROW:
+ return createGraphQLInputStructuredType(type, namePath, extendedScalarTypes);
+ case MAP:
+ return Optional.of(CustomScalars.JSON);
+ default:
+ return Optional.empty(); // Unsupported types are omitted
+ }
+ }
+
+ /**
+ * Creates a GraphQL input object type for a ROW type.
+ */
+ private static Optional createGraphQLInputStructuredType(RelDataType rowType, NamePath namePath, boolean extendedScalarTypes) {
+ GraphQLInputObjectType.Builder builder = GraphQLInputObjectType.newInputObject();
+ String typeName = uniquifyNameForPath(namePath, "Input");
+ builder.name(typeName);
+
+ for (RelDataTypeField field : rowType.getFieldList()) {
+ final NamePath fieldPath = namePath.concat(Name.system(field.getName()));
+ if (namePath.getLast().isHidden()) continue;
+ RelDataType type = field.getType();
+ getGraphQLInputType(type, fieldPath, extendedScalarTypes).map(t -> (GraphQLInputType) wrapNullable(t, type))
+ .ifPresent(fieldType -> builder.field(GraphQLInputObjectField.newInputObjectField()
+ .name(field.getName())
+ .type(fieldType)
+ .build()));
+ }
+ return Optional.of(builder.build());
+ }
+
+ /**
+ * Creates a GraphQL output object type for a ROW type.
+ */
+ private static Optional createGraphQLOutputStructuredType(RelDataType type, NamePath namePath, boolean extendedScalarTypes) {
+ GraphQLObjectType.Builder builder = GraphQLObjectType.newObject();
+ String typeName = uniquifyNameForPath(namePath, "Output");
+ builder.name(typeName);
+ for (RelDataTypeField field : type.getFieldList()) {
+ if (field.getName().startsWith(HIDDEN_PREFIX)) continue;
+ getOutputType(field.getType(), namePath.concat(Name.system(field.getName())), extendedScalarTypes) // recursively traverse
+ .ifPresent(fieldType -> builder.field(GraphQLFieldDefinition.newFieldDefinition()
+ .name(field.getName())
+ .type((GraphQLOutputType) wrapNullable(fieldType, field.getType()))
+ .build()
+ )
+ );
+ }
+ return Optional.of(builder.build());
+ }
+
+ public static String uniquifyNameForPath(NamePath fullPath) {
+ return fullPath.toString("_");
+ }
+
+ public static String uniquifyNameForPath(NamePath fullPath, String postfix) {
+ return fullPath.toString("_").concat(postfix);
+ }
+}
diff --git a/sqrl-planner/src/main/java/com/datasqrl/v2/graphql/GraphqlSchemaValidator2.java b/sqrl-planner/src/main/java/com/datasqrl/v2/graphql/GraphqlSchemaValidator2.java
new file mode 100644
index 0000000000..e2387ee02c
--- /dev/null
+++ b/sqrl-planner/src/main/java/com/datasqrl/v2/graphql/GraphqlSchemaValidator2.java
@@ -0,0 +1,370 @@
+package com.datasqrl.v2.graphql;
+
+import static com.datasqrl.graphql.server.TypeDefinitionRegistryUtil.getQueryTypeName;
+import static com.datasqrl.graphql.server.TypeDefinitionRegistryUtil.getType;
+import static com.datasqrl.graphql.util.GraphqlCheckUtil.checkState;
+import static com.datasqrl.graphql.util.GraphqlCheckUtil.createThrowable;
+
+import com.datasqrl.calcite.SqrlFramework;
+import com.datasqrl.calcite.function.SqrlTableMacro;
+import com.datasqrl.canonicalizer.Name;
+import com.datasqrl.canonicalizer.NamePath;
+import com.datasqrl.canonicalizer.ReservedName;
+import com.datasqrl.error.ErrorCollector;
+import com.datasqrl.graphql.APIConnectorManager;
+import com.datasqrl.graphql.generate.GraphqlSchemaUtil;
+import com.datasqrl.graphql.inference.SchemaWalker;
+import com.datasqrl.io.tables.TableSource;
+import com.datasqrl.plan.queries.APISource;
+import com.google.common.collect.Iterables;
+import com.google.inject.Inject;
+import graphql.language.EnumTypeDefinition;
+import graphql.language.FieldDefinition;
+import graphql.language.InputObjectTypeDefinition;
+import graphql.language.InputValueDefinition;
+import graphql.language.ListType;
+import graphql.language.NonNullType;
+import graphql.language.ObjectTypeDefinition;
+import graphql.language.ScalarTypeDefinition;
+import graphql.language.Type;
+import graphql.language.TypeDefinition;
+import graphql.language.TypeName;
+import graphql.schema.idl.SchemaParser;
+import graphql.schema.idl.TypeDefinitionRegistry;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import org.apache.calcite.jdbc.SqrlSchema;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeField;
+import org.apache.calcite.schema.Function;
+import org.apache.calcite.sql.validate.SqlNameMatcher;
+
+public class GraphqlSchemaValidator2 extends SchemaWalker {
+
+ private final Map visitedObj = new HashMap<>();
+ private final ErrorCollector errorCollector;
+
+ @Inject
+ public GraphqlSchemaValidator2(SqrlFramework framework, APIConnectorManager apiConnectorManager, ErrorCollector errorCollector) {
+ this(framework.getCatalogReader().nameMatcher(), framework.getSchema(), apiConnectorManager, errorCollector);
+ }
+
+ public GraphqlSchemaValidator2(SqlNameMatcher nameMatcher, SqrlSchema schema, APIConnectorManager apiManager, ErrorCollector errorCollector) {
+ super(nameMatcher, schema, apiManager);
+ this.errorCollector = errorCollector;
+ }
+
+ @Override
+ protected void walkSubscription(ObjectTypeDefinition m, FieldDefinition fieldDefinition,
+ TypeDefinitionRegistry registry, APISource source) {
+ //Assure they are root tables
+ Collection functions = schema.getFunctions(fieldDefinition.getName(), false);
+ checkState(functions.size() == 1, fieldDefinition.getSourceLocation(),
+ "Cannot overload subscription");
+ Function function = Iterables.getOnlyElement(functions);
+ checkState(function instanceof SqrlTableMacro, fieldDefinition.getSourceLocation(),
+ "Subscription not a sqrl table");
+
+ //todo: validate that it is a valid table
+
+ }
+
+ @Override
+ protected void walkMutation(APISource source, TypeDefinitionRegistry registry,
+ ObjectTypeDefinition m, FieldDefinition fieldDefinition) {
+ // Check we've found the mutation
+ TableSource mutationSink = apiManager.getMutationSource(source,
+ Name.system(fieldDefinition.getName()));
+ if (mutationSink == null) {
+// throw createThrowable(fieldDefinition.getSourceLocation(),
+// "Could not find mutation source: %s.", fieldDefinition.getName());
+ }
+
+ validateStructurallyEqualMutation(fieldDefinition, getValidMutationReturnType(fieldDefinition, registry),
+ getValidMutationInput(fieldDefinition, registry),
+ List.of(ReservedName.MUTATION_TIME.getCanonical(), ReservedName.MUTATION_PRIMARY_KEY.getDisplay()), registry);
+
+ }
+
+ private Object validateStructurallyEqualMutation(FieldDefinition fieldDefinition,
+ ObjectTypeDefinition returnTypeDefinition, InputObjectTypeDefinition inputType,
+ List allowedFieldNames, TypeDefinitionRegistry registry) {
+
+ //The return type can have event_time
+ for (FieldDefinition returnTypeFieldDefinition : returnTypeDefinition.getFieldDefinitions()) {
+ if (allowedFieldNames.contains(returnTypeFieldDefinition.getName())) {
+ continue;
+ }
+
+ String name = returnTypeFieldDefinition.getName();
+ InputValueDefinition inputDefinition = findExactlyOneInputValue(fieldDefinition, name,
+ inputType.getInputValueDefinitions());
+
+ //validate type structurally equal
+ validateStructurallyEqualMutation(returnTypeFieldDefinition, inputDefinition, registry);
+ }
+
+ return null;
+ }
+
+ private void validateStructurallyEqualMutation(FieldDefinition fieldDefinition,
+ InputValueDefinition inputDefinition, TypeDefinitionRegistry registry) {
+ checkState(fieldDefinition.getName().equals(inputDefinition.getName()),
+ fieldDefinition.getSourceLocation(), "Name must be equal to the input name {} {}",
+ fieldDefinition.getName(), inputDefinition.getName());
+ Type definitionType = fieldDefinition.getType();
+ Type inputType = inputDefinition.getType();
+
+ validateStructurallyType(fieldDefinition, definitionType, inputType, registry);
+ }
+
+ private Object validateStructurallyType(FieldDefinition field, Type definitionType,
+ Type inputType, TypeDefinitionRegistry registry) {
+ if (inputType instanceof NonNullType) {
+ //subType may be nullable if type is non-null
+ NonNullType nonNullType = (NonNullType) inputType;
+ if (definitionType instanceof NonNullType) {
+ NonNullType nonNullDefinitionType = (NonNullType) definitionType;
+ return validateStructurallyType(field, nonNullDefinitionType.getType(),
+ nonNullType.getType(), registry);
+ } else {
+ return validateStructurallyType(field, definitionType, nonNullType.getType(), registry);
+ }
+ } else if (inputType instanceof ListType) {
+ //subType must be a list
+ checkState(definitionType instanceof ListType, definitionType.getSourceLocation(),
+ "List type mismatch for field. Must match the input type. " + field.getName());
+ ListType inputListType = (ListType) inputType;
+ ListType definitionListType = (ListType) definitionType;
+ return validateStructurallyType(field, definitionListType.getType(), inputListType.getType(), registry);
+ } else if (inputType instanceof TypeName) {
+ //If subtype nonnull then it could return errors
+ checkState(!(definitionType instanceof NonNullType), definitionType.getSourceLocation(),
+ "Non-null found on field %s, could result in errors if input type is null",
+ field.getName());
+ checkState(!(definitionType instanceof ListType), definitionType.getSourceLocation(),
+ "List type found on field %s when the input is a scalar type", field.getName());
+
+ //If typeName, resolve then
+ TypeName inputTypeName = (TypeName) inputType;
+ TypeName defTypeName = (TypeName) unboxNonNull(definitionType);
+ TypeDefinition inputTypeDef = registry.getType(inputTypeName).orElseThrow(
+ () -> createThrowable(inputTypeName.getSourceLocation(), "Could not find type: %s",
+ inputTypeName.getName()));
+ TypeDefinition defTypeDef = registry.getType(defTypeName).orElseThrow(
+ () -> createThrowable(defTypeName.getSourceLocation(), "Could not find type: %s",
+ defTypeName.getName()));
+
+ //If input or scalar
+ if (inputTypeDef instanceof ScalarTypeDefinition) {
+ checkState(defTypeDef instanceof ScalarTypeDefinition && inputTypeDef.getName()
+ .equals(defTypeDef.getName()), field.getSourceLocation(),
+ "Scalar types not matching for field [%s]: found %s but wanted %s", field.getName(),
+ inputTypeDef.getName(), defTypeDef.getName());
+ return null;
+ } else if (inputTypeDef instanceof EnumTypeDefinition) {
+ checkState(defTypeDef instanceof EnumTypeDefinition
+ || defTypeDef instanceof ScalarTypeDefinition && inputTypeDef.getName()
+ .equals(defTypeDef.getName()), field.getSourceLocation(),
+ "Enum types not matching for field [%s]: found %s but wanted %s", field.getName(),
+ inputTypeDef.getName(), defTypeDef.getName());
+ return null;
+ } else if (inputTypeDef instanceof InputObjectTypeDefinition) {
+ checkState(defTypeDef instanceof ObjectTypeDefinition, field.getSourceLocation(),
+ "Return object type must match with an input object type not matching for field [%s]: found %s but wanted %s",
+ field.getName(), inputTypeDef.getName(), defTypeDef.getName());
+ ObjectTypeDefinition objectDefinition = (ObjectTypeDefinition) defTypeDef;
+ InputObjectTypeDefinition inputDefinition = (InputObjectTypeDefinition) inputTypeDef;
+ return validateStructurallyEqualMutation(field, objectDefinition, inputDefinition,
+ List.of(), registry);
+ } else {
+ throw createThrowable(inputTypeDef.getSourceLocation(), "Unknown type encountered: %s",
+ inputTypeDef.getName());
+ }
+ }
+
+ throw createThrowable(field.getSourceLocation(), "Unknown type encountered for field: %s",
+ field.getName());
+ }
+
+ private InputValueDefinition findExactlyOneInputValue(FieldDefinition fieldDefinition,
+ String name, List inputValueDefinitions) {
+ InputValueDefinition found = null;
+ for (InputValueDefinition inputDefinition : inputValueDefinitions) {
+ if (inputDefinition.getName().equals(name)) {
+ checkState(found == null, inputDefinition.getSourceLocation(), "Duplicate fields found");
+ found = inputDefinition;
+ }
+ }
+
+ checkState(found != null, fieldDefinition.getSourceLocation(),
+ "Could not find field %s in type %s", name, fieldDefinition.getName());
+
+ return found;
+ }
+
+ private InputObjectTypeDefinition getValidMutationInput(FieldDefinition fieldDefinition, TypeDefinitionRegistry registry) {
+ checkState(!(fieldDefinition.getInputValueDefinitions().isEmpty()),
+ fieldDefinition.getSourceLocation(), fieldDefinition.getName()
+ + " has too few arguments. Must have one non-null input type argument.");
+ checkState(fieldDefinition.getInputValueDefinitions().size() == 1,
+ fieldDefinition.getSourceLocation(), fieldDefinition.getName()
+ + " has too many arguments. Must have one non-null input type argument.");
+ checkState(fieldDefinition.getInputValueDefinitions().get(0).getType() instanceof NonNullType,
+ fieldDefinition.getSourceLocation(),
+ "[" + fieldDefinition.getName() + "] " + fieldDefinition.getInputValueDefinitions().get(0)
+ .getName() + "Must be non-null.");
+ NonNullType nonNullType = (NonNullType) fieldDefinition.getInputValueDefinitions().get(0)
+ .getType();
+ checkState(nonNullType.getType() instanceof TypeName, fieldDefinition.getSourceLocation(),
+ "Must be a singular value");
+ TypeName name = (TypeName) nonNullType.getType();
+
+ Optional typeDef = registry.getType(name);
+ checkState(typeDef.isPresent(), fieldDefinition.getSourceLocation(),
+ "Could not find input type:" + name.getName());
+ checkState(typeDef.get() instanceof InputObjectTypeDefinition,
+ fieldDefinition.getSourceLocation(),
+ "Input must be an input object type:" + fieldDefinition.getName());
+
+ return (InputObjectTypeDefinition) typeDef.get();
+ }
+
+
+ private ObjectTypeDefinition getValidMutationReturnType(FieldDefinition fieldDefinition, TypeDefinitionRegistry registry) {
+ Type type = fieldDefinition.getType();
+ if (type instanceof NonNullType) {
+ type = ((NonNullType) type).getType();
+ }
+
+ checkState(type instanceof TypeName, type.getSourceLocation(),
+ "[%s] must be a singular return value", fieldDefinition.getName());
+ TypeName name = (TypeName) type;
+
+ TypeDefinition typeDef = registry.getType(name).orElseThrow(
+ () -> createThrowable(name.getSourceLocation(), "Could not find return type: %s", name.getName()));
+ checkState(typeDef instanceof ObjectTypeDefinition, typeDef.getSourceLocation(),
+ "Return must be an object type: %s", fieldDefinition.getName());
+
+ return (ObjectTypeDefinition) typeDef;
+ }
+
+ @Override
+ protected void visitUnknownObject(ObjectTypeDefinition type, FieldDefinition field, NamePath path,
+ Optional rel) {
+ throw createThrowable(field.getSourceLocation(), "Unknown field at location %s", rel.map(
+ r -> field.getName() + ". Possible scalars are [" + r.getFieldNames().stream()
+ .filter(GraphqlSchemaUtil::isValidGraphQLName).collect(Collectors.joining(", ")) + "]")
+ .orElse(field.getName()));
+ }
+
+ @Override
+ protected void visitScalar(ObjectTypeDefinition type, FieldDefinition field, NamePath path,
+ RelDataType relDataType, RelDataTypeField relDataTypeField) {
+ }
+
+ @Override
+ protected void visitQuery(ObjectTypeDefinition parentType, ObjectTypeDefinition type,
+ FieldDefinition field, NamePath path, Optional rel,
+ List functions) {
+ checkState(!functions.isEmpty(), field.getSourceLocation(), "Could not find functions");
+ checkValidArrayNonNullType(field.getType());
+
+// if (visitedObj.get(type) != null && !visitedObj.get(type).getIsTypeOf()
+// .isEmpty()) {
+ //todo readd the check to see if we can share a type
+// if (!sqrlTable.getIsTypeOf()
+// .contains(visitedObj.get(objectDefinition).getIsTypeOf().get(0))) {
+// checkState(visitedObj.get(parentType) == null,
+// || visitedObj.get(objectDefinition) == sqrlTable,
+// field.getSourceLocation(),
+// "Cannot redefine a type to point to a different SQRL table. Use an interface instead.\n"
+// + "The graphql field [%s] points to Sqrl table [%s] but already had [%s].",
+// parentType.getName() + ":" + field.getName(),
+// functions.get(0).getFullPath().getDisplay(),
+// visitedObj.get(parentType) == null ? null : visitedObj.get(parentType).getFullPath().getDisplay());
+// }
+// }
+ visitedObj.put(parentType, functions.get(0));
+
+ //todo: better structural checking
+// walkChildren((ObjectTypeDefinition) type, functions.get(0), field);
+// List invalidFields = getInvalidFields(typeDef, table);
+// boolean structurallyEqual = structurallyEqual(typeDef, table);
+// //todo clean up, add lazy evaluation
+// checkState(structurallyEqual, invalidFields.isEmpty() ? typeDef.getSourceLocation()
+// : invalidFields.get(invalidFields.size() - 1).getSourceLocation(),
+// "Field(s) [%s] could not be found on type [%s]. Possible fields are: [%s]", String.join(",",
+// invalidFields.stream().map(FieldDefinition::getName).collect(Collectors.toList())),
+// typeDef.getName(), String.join(", ", table.tableMacro.getRowType().getFieldNames()));
+ }
+
+
+ private void checkValidArrayNonNullType(Type type) {
+ Type root = type;
+ if (type instanceof NonNullType) {
+ type = ((NonNullType) type).getType();
+ }
+ if (type instanceof ListType) {
+ type = ((ListType) type).getType();
+ }
+ if (type instanceof NonNullType) {
+ type = ((NonNullType) type).getType();
+ }
+ checkState(type instanceof TypeName, root.getSourceLocation(),
+ "Type must be a non-null array, array, or non-null");
+ }
+
+// private boolean structurallyEqual(ImplementingTypeDefinition typeDef, SQRLTable table) {
+// return typeDef.getFieldDefinitions().stream()
+// .allMatch(f -> table.getField(Name.system(((NamedNode) f).getName())).isPresent());
+// }
+//
+// private List getInvalidFields(ObjectTypeDefinition typeDef, SQRLTable table) {
+// return typeDef.getFieldDefinitions().stream()
+// .filter(f -> table.getField(Name.system(f.getName())).isEmpty())
+// .collect(Collectors.toList());
+// }
+
+
+ private TypeDefinition unwrapObjectType(Type type, TypeDefinitionRegistry registry) {
+ //type can be in a single array with any non-nulls, e.g. [customer!]!
+ type = unboxNonNull(type);
+ if (type instanceof ListType) {
+ type = ((ListType) type).getType();
+ }
+ type = unboxNonNull(type);
+
+ Optional typeDef = registry.getType(type);
+
+ checkState(typeDef.isPresent(), type.getSourceLocation(), "Could not find Object type [%s]",
+ type instanceof TypeName ? ((TypeName) type).getName() : type.toString());
+
+ return typeDef.get();
+ }
+
+ private Type unboxNonNull(Type type) {
+ if (type instanceof NonNullType) {
+ return unboxNonNull(((NonNullType) type).getType());
+ }
+ return type;
+ }
+
+ public void validate(APISource source) {
+ try {
+ TypeDefinitionRegistry registry = (new SchemaParser()).parse(source.getSchemaDefinition());
+ Optional queryType = getType(registry, () -> getQueryTypeName(registry));
+ if (queryType.isEmpty()) {
+ throw createThrowable(null, "Cannot find graphql Query type");
+ }
+
+ walk(source);
+ } catch (Exception e) {
+ throw errorCollector.handle(e);
+ }
+ }
+}
diff --git a/sqrl-planner/src/main/java/com/datasqrl/v2/graphql/InferGraphqlSchema2.java b/sqrl-planner/src/main/java/com/datasqrl/v2/graphql/InferGraphqlSchema2.java
new file mode 100644
index 0000000000..2547ac5d95
--- /dev/null
+++ b/sqrl-planner/src/main/java/com/datasqrl/v2/graphql/InferGraphqlSchema2.java
@@ -0,0 +1,77 @@
+package com.datasqrl.v2.graphql;
+
+import com.datasqrl.calcite.SqrlFramework;
+import com.datasqrl.canonicalizer.NameCanonicalizer;
+import com.datasqrl.engine.pipeline.ExecutionPipeline;
+import com.datasqrl.engine.server.ServerPhysicalPlan;
+import com.datasqrl.error.ErrorCollector;
+import com.datasqrl.graphql.APIConnectorManager;
+import com.datasqrl.graphql.GraphqlSchemaParser;
+import com.datasqrl.graphql.inference.GraphqlQueryBuilder;
+import com.datasqrl.graphql.inference.GraphqlQueryGenerator;
+import com.datasqrl.plan.queries.APISource;
+import com.datasqrl.plan.queries.APISubscription;
+import com.datasqrl.util.SqlNameUtil;
+import com.google.inject.Inject;
+import graphql.schema.GraphQLSchema;
+import graphql.schema.GraphqlTypeComparatorRegistry;
+import graphql.schema.idl.SchemaPrinter;
+
+import java.util.*;
+import lombok.AllArgsConstructor;
+import lombok.SneakyThrows;
+
+/**
+ * Creates new table functions from the GraphQL schema.
+ */
+@AllArgsConstructor(onConstructor_ = @Inject)
+public class InferGraphqlSchema2 {
+
+ private final ExecutionPipeline pipeline;
+ private final SqrlFramework framework;
+ private final ErrorCollector errorCollector;
+ private final APIConnectorManager apiManager;
+ private final GraphqlSchemaFactory2 graphqlSchemaFactory;
+ private final GraphqlSchemaParser parser;
+
+ @SneakyThrows
+ public Optional inferGraphQLSchema(ServerPhysicalPlan serverPlan) {
+ Optional gqlSchema = graphqlSchemaFactory.generate(serverPlan);
+
+ SchemaPrinter.Options opts = SchemaPrinter.Options.defaultOptions()
+ .setComparators(GraphqlTypeComparatorRegistry.AS_IS_REGISTRY)
+ .includeDirectives(false);
+
+ return gqlSchema.map(schema -> new SchemaPrinter(opts).print(schema));
+ }
+
+
+ private ErrorCollector createErrorCollectorWithSchema(APISource apiSource) {
+ return errorCollector.withSchema(apiSource.getName().getDisplay(), apiSource.getSchemaDefinition());
+ }
+
+ // Validates the schema and generates queries and subscriptions
+ public void validateAndGenerateQueries(APISource apiSource) {
+ GraphqlSchemaValidator2 schemaValidator = new GraphqlSchemaValidator2(framework, apiManager, createErrorCollectorWithSchema(apiSource));
+ schemaValidator.validate(apiSource);
+
+ GraphqlQueryGenerator queryGenerator = new GraphqlQueryGenerator(
+ framework.getCatalogReader().nameMatcher(),
+ framework.getSchema(),
+ new GraphqlQueryBuilder(framework, apiManager, new SqlNameUtil(NameCanonicalizer.SYSTEM)),
+ apiManager
+ );
+
+ queryGenerator.walk(apiSource);
+
+ // Add queries to apiManager
+ queryGenerator.getQueries().forEach(apiManager::addQuery);
+
+ // Add subscriptions to apiManager
+ final APISource source = apiSource;
+ queryGenerator.getSubscriptions().forEach(subscription ->
+ apiManager.addSubscription(
+ new APISubscription(subscription.getAbsolutePath().getFirst(), source), subscription)
+ );
+ }
+}
diff --git a/sqrl-planner/src/test/java/com/datasqrl/calcite/DataTypeTest.java b/sqrl-planner/src/test/java/com/datasqrl/calcite/DataTypeTest.java
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/sqrl-server/sqrl-server-core/pom.xml b/sqrl-server/sqrl-server-core/pom.xml
index e9d74b210a..c0ad09cda4 100644
--- a/sqrl-server/sqrl-server-core/pom.xml
+++ b/sqrl-server/sqrl-server-core/pom.xml
@@ -32,7 +32,6 @@
com.graphql-java
graphql-java-extended-scalars
- 19.1
diff --git a/sqrl-server/sqrl-server-core/src/main/java/com/datasqrl/graphql/server/CustomScalars.java b/sqrl-server/sqrl-server-core/src/main/java/com/datasqrl/graphql/server/CustomScalars.java
index ebca23cb06..bca054ff76 100644
--- a/sqrl-server/sqrl-server-core/src/main/java/com/datasqrl/graphql/server/CustomScalars.java
+++ b/sqrl-server/sqrl-server-core/src/main/java/com/datasqrl/graphql/server/CustomScalars.java
@@ -45,4 +45,5 @@ public Object parseLiteral(Object input) {
public static final GraphQLScalarType DATE = ExtendedScalars.Date;
public static final GraphQLScalarType TIME = ExtendedScalars.LocalTime;
public static final GraphQLScalarType JSON = ExtendedScalars.Json;
+ public static final GraphQLScalarType GRAPHQL_BIGINTEGER = ExtendedScalars.GraphQLBigInteger.transform(builder->builder.name("GraphQLBigInteger"));
}
\ No newline at end of file
diff --git a/sqrl-server/sqrl-server-core/src/main/java/com/datasqrl/graphql/server/GraphQLEngineBuilder.java b/sqrl-server/sqrl-server-core/src/main/java/com/datasqrl/graphql/server/GraphQLEngineBuilder.java
index ad1f4c5527..4ac7179144 100644
--- a/sqrl-server/sqrl-server-core/src/main/java/com/datasqrl/graphql/server/GraphQLEngineBuilder.java
+++ b/sqrl-server/sqrl-server-core/src/main/java/com/datasqrl/graphql/server/GraphQLEngineBuilder.java
@@ -59,7 +59,7 @@ public class GraphQLEngineBuilder implements
QueryBaseVisitor,
ResolvedQueryVisitor {
- private final List addlTypes;
+ private final List extendedScalarTypes;
private final SubscriptionConfiguration> subscriptionConfiguration;
private final MutationConfiguration> mutationConfiguration;
@@ -72,18 +72,18 @@ public class GraphQLEngineBuilder implements
.build();
private GraphQLEngineBuilder(Builder builder) {
- this.addlTypes = builder.addlTypes;
+ this.extendedScalarTypes = builder.extendedScalarTypes;
this.subscriptionConfiguration = builder.subscriptionConfiguration;
this.mutationConfiguration = builder.mutationConfiguration;
}
public static class Builder {
- private List addlTypes = new ArrayList<>();
+ private List extendedScalarTypes = new ArrayList<>();
private SubscriptionConfiguration> subscriptionConfiguration;
private MutationConfiguration> mutationConfiguration;
- public Builder withAdditionalTypes(List types) {
- this.addlTypes = types;
+ public Builder withExtendedScalarTypes(List types) {
+ this.extendedScalarTypes = types;
return this;
}
@@ -157,7 +157,7 @@ private RuntimeWiring createWiring(TypeDefinitionRegistry registry, GraphQLCodeR
.scalar(CustomScalars.JSON)
;
- addlTypes.forEach(t->wiring.scalar(t));
+ extendedScalarTypes.forEach(t->wiring.scalar(t));
for (Map.Entry typeEntry : registry.types().entrySet()) {
if (typeEntry.getValue() instanceof InterfaceTypeDefinition) {
diff --git a/sqrl-server/sqrl-server-vertx/src/main/java/com/datasqrl/graphql/GraphQLServer.java b/sqrl-server/sqrl-server-vertx/src/main/java/com/datasqrl/graphql/GraphQLServer.java
index e9c3e63ecc..696b152bd3 100644
--- a/sqrl-server/sqrl-server-vertx/src/main/java/com/datasqrl/graphql/GraphQLServer.java
+++ b/sqrl-server/sqrl-server-vertx/src/main/java/com/datasqrl/graphql/GraphQLServer.java
@@ -6,9 +6,9 @@
import com.datasqrl.canonicalizer.NameCanonicalizer;
import com.datasqrl.graphql.config.CorsHandlerOptions;
import com.datasqrl.graphql.config.ServerConfig;
+import com.datasqrl.graphql.server.CustomScalars;
import com.datasqrl.graphql.server.GraphQLEngineBuilder;
import com.datasqrl.graphql.server.RootGraphqlModel;
-import com.datasqrl.graphql.type.SqrlVertxScalars;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.google.common.base.Strings;
@@ -331,8 +331,8 @@ public GraphQL createGraphQL(Map client, Promise startP
.withMutationConfiguration(
new MutationConfigurationImpl(model, vertx, config))
.withSubscriptionConfiguration(
- new SubscriptionConfigurationImpl(model, vertx, config, startPromise, vertxJdbcClient)
- )
+ new SubscriptionConfigurationImpl(model, vertx, config, startPromise, vertxJdbcClient))
+ .withExtendedScalarTypes(List.of(CustomScalars.GRAPHQL_BIGINTEGER))
.build(),
new VertxContext(vertxJdbcClient, canonicalizer));
MeterRegistry meterRegistry = BackendRegistries.getDefaultNow();
diff --git a/sqrl-testing/sqrl-integration-tests/src/test/resources/dagplanner/importsWithNestedType.sqrl b/sqrl-testing/sqrl-integration-tests/src/test/resources/dagplanner/importsWithNestedType.sqrl
new file mode 100644
index 0000000000..993de36a1d
--- /dev/null
+++ b/sqrl-testing/sqrl-integration-tests/src/test/resources/dagplanner/importsWithNestedType.sqrl
@@ -0,0 +1,3 @@
+IMPORT ecommerceTs.Orders;
+
+
diff --git a/sqrl-testing/sqrl-integration-tests/src/test/resources/dagplanner/relationship-fail.sqrl b/sqrl-testing/sqrl-integration-tests/src/test/resources/dagplanner/relationship-fail.sqrl
new file mode 100644
index 0000000000..a8b84791b0
--- /dev/null
+++ b/sqrl-testing/sqrl-integration-tests/src/test/resources/dagplanner/relationship-fail.sqrl
@@ -0,0 +1,13 @@
+IMPORT ecommerceTs.Customer; --base table = Customer
+-- Orders has nested data `entries` (see orders.schema.yml)
+IMPORT ecommerceTs.Orders; --base table = Orders
+
+rootFunction($i : INT) := SELECT id FROM Orders WHERE id = $i; -- no base table
+rootFunctionWithBaseTable($i : INT) := SELECT * FROM Orders WHERE id = $i; -- base table = Orders
+
+-- A relationship, `this.customerid` gets replaced with an internal parameter that gets substituted with customerid on the parent table Customer
+Customer.orders := SELECT * FROM Orders o WHERE this.customerid = o.customerid; --base table
+Customer.orders2 := SELECT id FROM Orders o WHERE this.customerid = o.customerid; --no base table
+
+-- cannot have a relationship in the FROM clause.
+-- CustomerEntries($customerid : BIGINT) := SELECT entries e FROM Customer.orders customerOrders WHERE customerOrders.customerid = $customerid;
\ No newline at end of file
diff --git a/sqrl-testing/sqrl-integration-tests/src/test/resources/dagplanner/relationship-subscribe.sqrl b/sqrl-testing/sqrl-integration-tests/src/test/resources/dagplanner/relationship-subscribe.sqrl
new file mode 100644
index 0000000000..10a75c773e
--- /dev/null
+++ b/sqrl-testing/sqrl-integration-tests/src/test/resources/dagplanner/relationship-subscribe.sqrl
@@ -0,0 +1,15 @@
+IMPORT ecommerceTs.Customer; --base table = Customer
+-- Orders has nested data `entries` (see orders.schema.yml)
+IMPORT ecommerceTs.Orders; --base table = Orders
+
+-- projections are not supported on kafka engine for subscriptions
+rootFunction($i : INT) := SELECT id FROM Orders WHERE id = $i; -- no base table
+rootFunctionWithBaseTable($i : INT) := SUBSCRIBE SELECT * FROM Orders WHERE id = $i; -- base table = Orders
+rootFunctionWithBaseTable2($i : INT) := SELECT * FROM Orders WHERE id = $i; -- base table = Orders
+
+-- A relationship, `this.customerid` gets replaced with an internal parameter that gets substituted with customerid on the parent table Customer
+Customer.orders := SELECT * FROM Orders o WHERE this.customerid = o.customerid; --base table
+Customer.orders2($i : INT) := SELECT id FROM Orders o WHERE this.customerid = o.customerid AND id = $i; --no base table
+
+-- cannot have a relationship in the FROM clause.
+-- CustomerEntries($customerid : BIGINT) := SELECT entries e FROM Customer.orders customerOrders WHERE customerOrders.customerid = $customerid;
\ No newline at end of file
diff --git a/sqrl-testing/sqrl-integration-tests/src/test/resources/dagplanner/relationship.sqrl b/sqrl-testing/sqrl-integration-tests/src/test/resources/dagplanner/relationship.sqrl
new file mode 100644
index 0000000000..c9df8317d9
--- /dev/null
+++ b/sqrl-testing/sqrl-integration-tests/src/test/resources/dagplanner/relationship.sqrl
@@ -0,0 +1,13 @@
+IMPORT ecommerceTs.Customer; --base table = Customer
+-- Orders has nested data `entries` (see orders.schema.yml)
+IMPORT ecommerceTs.Orders; --base table = Orders
+
+rootFunction($i : INT) := SELECT id FROM Orders WHERE id = $i; -- no base table
+rootFunctionWithBaseTable($i : INT) := SELECT * FROM Orders WHERE id = $i; -- base table = Orders
+
+-- A relationship, `this.customerid` gets replaced with an internal parameter that gets substituted with customerid on the parent table Customer
+Customer.orders := SELECT * FROM Orders o WHERE this.customerid = o.customerid; --base table
+Customer.orders2($i : INT) := SELECT id FROM Orders o WHERE this.customerid = o.customerid AND id = $i; --no base table
+
+-- cannot have a relationship in the FROM clause.
+-- CustomerEntries($customerid : BIGINT) := SELECT entries e FROM Customer.orders customerOrders WHERE customerOrders.customerid = $customerid;
\ No newline at end of file
diff --git a/sqrl-testing/sqrl-integration-tests/src/test/resources/snapshots/com/datasqrl/UseCaseCompileTest/simple-with-bigint-support--package.txt b/sqrl-testing/sqrl-integration-tests/src/test/resources/snapshots/com/datasqrl/UseCaseCompileTest/simple-with-bigint-support--package.txt
new file mode 100644
index 0000000000..6a10fdace4
--- /dev/null
+++ b/sqrl-testing/sqrl-integration-tests/src/test/resources/snapshots/com/datasqrl/UseCaseCompileTest/simple-with-bigint-support--package.txt
@@ -0,0 +1,397 @@
+>>>pipeline_explain.txt
+=== Orders
+ID: orders_2
+Type: stream
+Stage: flink
+Primary Key: id, customerid, time
+Timestamp : _ingest_time
+Schema:
+ - id: BIGINT NOT NULL
+ - customerid: BIGINT NOT NULL
+ - time: TIMESTAMP_WITH_LOCAL_TIME_ZONE(3) NOT NULL
+ - productid: BIGINT NOT NULL
+ - quantity: BIGINT NOT NULL
+ - unit_price: DOUBLE
+ - discount: DOUBLE
+ - _ingest_time: TIMESTAMP_LTZ(3) *PROCTIME* NOT NULL
+Plan:
+LogicalTableScan(table=[[orders_1]])
+
+>>>flink.json
+{
+ "flinkSql" : [
+ "CREATE TEMPORARY TABLE `orders_1` (\n `id` BIGINT NOT NULL,\n `customerid` BIGINT NOT NULL,\n `time` TIMESTAMP(3) WITH LOCAL TIME ZONE NOT NULL,\n `productid` BIGINT NOT NULL,\n `quantity` BIGINT NOT NULL,\n `unit_price` DOUBLE,\n `discount` DOUBLE,\n `_ingest_time` AS PROCTIME(),\n PRIMARY KEY (`id`, `customerid`, `time`) NOT ENFORCED\n) WITH (\n 'format' = 'flexible-json',\n 'path' = '${DATA_PATH}/orders.jsonl',\n 'source.monitor-interval' = '10000',\n 'connector' = 'filesystem'\n);",
+ "CREATE TEMPORARY TABLE `orders_2` (\n `id` BIGINT NOT NULL,\n `customerid` BIGINT NOT NULL,\n `time` TIMESTAMP(3) WITH LOCAL TIME ZONE NOT NULL,\n `productid` BIGINT NOT NULL,\n `quantity` BIGINT NOT NULL,\n `unit_price` DOUBLE,\n `discount` DOUBLE,\n `_ingest_time` TIMESTAMP(3) WITH LOCAL TIME ZONE NOT NULL,\n PRIMARY KEY (`id`, `customerid`, `time`) NOT ENFORCED\n) WITH (\n 'password' = '${JDBC_PASSWORD}',\n 'connector' = 'jdbc-sqrl',\n 'driver' = 'org.postgresql.Driver',\n 'table-name' = 'orders_2',\n 'url' = '${JDBC_URL}',\n 'username' = '${JDBC_USERNAME}'\n);",
+ "CREATE VIEW `table$1`\nAS\nSELECT *\nFROM `orders_1`;",
+ "EXECUTE STATEMENT SET BEGIN\nINSERT INTO `orders_2`\n(SELECT *\n FROM `table$1`)\n;\nEND;"
+ ],
+ "connectors" : [
+ "jdbc-sqrl",
+ "filesystem"
+ ],
+ "formats" : [
+ "flexible-json"
+ ]
+}
+>>>kafka.json
+{
+ "topics" : [ ]
+}
+>>>postgres.json
+{
+ "ddl" : [
+ {
+ "name" : "orders_2",
+ "columns" : [
+ "\"id\" BIGINT NOT NULL",
+ "\"customerid\" BIGINT NOT NULL",
+ "\"time\" TIMESTAMP WITH TIME ZONE NOT NULL",
+ "\"productid\" BIGINT NOT NULL",
+ "\"quantity\" BIGINT NOT NULL",
+ "\"unit_price\" DOUBLE PRECISION ",
+ "\"discount\" DOUBLE PRECISION ",
+ "\"_ingest_time\" TIMESTAMP WITH TIME ZONE NOT NULL"
+ ],
+ "primaryKeys" : [
+ "\"id\"",
+ "\"customerid\"",
+ "\"time\""
+ ],
+ "sql" : "CREATE TABLE IF NOT EXISTS orders_2 (\"id\" BIGINT NOT NULL,\"customerid\" BIGINT NOT NULL,\"time\" TIMESTAMP WITH TIME ZONE NOT NULL,\"productid\" BIGINT NOT NULL,\"quantity\" BIGINT NOT NULL,\"unit_price\" DOUBLE PRECISION ,\"discount\" DOUBLE PRECISION ,\"_ingest_time\" TIMESTAMP WITH TIME ZONE NOT NULL , PRIMARY KEY (\"id\",\"customerid\",\"time\"));"
+ },
+ {
+ "indexName" : "orders_2_btree_c1c2",
+ "tableName" : "orders_2",
+ "columns" : [
+ "customerid",
+ "time"
+ ],
+ "type" : "BTREE",
+ "sql" : "CREATE INDEX IF NOT EXISTS orders_2_btree_c1c2 ON orders_2 USING btree (\"customerid\",\"time\");"
+ },
+ {
+ "indexName" : "orders_2_btree_c2c0",
+ "tableName" : "orders_2",
+ "columns" : [
+ "time",
+ "id"
+ ],
+ "type" : "BTREE",
+ "sql" : "CREATE INDEX IF NOT EXISTS orders_2_btree_c2c0 ON orders_2 USING btree (\"time\",\"id\");"
+ }
+ ],
+ "views" : [
+ {
+ "name" : "Orders",
+ "sql" : "CREATE OR REPLACE VIEW \"Orders\"(\"id\", \"customerid\", \"time\", \"productid\", \"quantity\", \"unit_price\", \"discount\", \"_ingest_time\") AS SELECT *\nFROM \"orders_2\"\nORDER BY \"_ingest_time\" DESC NULLS LAST, \"id\", \"customerid\", \"time\";"
+ }
+ ]
+}
+>>>vertx.json
+{
+ "model" : {
+ "coords" : [
+ {
+ "type" : "args",
+ "parentType" : "Query",
+ "fieldName" : "Orders",
+ "matchs" : [
+ {
+ "arguments" : [
+ {
+ "type" : "variable",
+ "type" : "variable",
+ "path" : "time"
+ },
+ {
+ "type" : "variable",
+ "type" : "variable",
+ "path" : "limit"
+ },
+ {
+ "type" : "variable",
+ "type" : "variable",
+ "path" : "offset"
+ }
+ ],
+ "query" : {
+ "type" : "PagedJdbcQuery",
+ "type" : "PagedJdbcQuery",
+ "sql" : "SELECT *\nFROM \"orders_2\"\nWHERE \"time\" = $1\nORDER BY \"_ingest_time\" DESC NULLS LAST, \"id\", \"customerid\"",
+ "parameters" : [
+ {
+ "type" : "arg",
+ "type" : "arg",
+ "path" : "time"
+ }
+ ]
+ }
+ },
+ {
+ "arguments" : [
+ {
+ "type" : "variable",
+ "type" : "variable",
+ "path" : "limit"
+ },
+ {
+ "type" : "variable",
+ "type" : "variable",
+ "path" : "offset"
+ }
+ ],
+ "query" : {
+ "type" : "PagedJdbcQuery",
+ "type" : "PagedJdbcQuery",
+ "sql" : "SELECT *\nFROM \"orders_2\"\nORDER BY \"_ingest_time\" DESC NULLS LAST, \"id\", \"customerid\", \"time\"",
+ "parameters" : [ ]
+ }
+ },
+ {
+ "arguments" : [
+ {
+ "type" : "variable",
+ "type" : "variable",
+ "path" : "id"
+ },
+ {
+ "type" : "variable",
+ "type" : "variable",
+ "path" : "time"
+ },
+ {
+ "type" : "variable",
+ "type" : "variable",
+ "path" : "limit"
+ },
+ {
+ "type" : "variable",
+ "type" : "variable",
+ "path" : "offset"
+ }
+ ],
+ "query" : {
+ "type" : "PagedJdbcQuery",
+ "type" : "PagedJdbcQuery",
+ "sql" : "SELECT *\nFROM \"orders_2\"\nWHERE \"id\" = $1 AND \"time\" = $2\nORDER BY \"_ingest_time\" DESC NULLS LAST, \"customerid\"",
+ "parameters" : [
+ {
+ "type" : "arg",
+ "type" : "arg",
+ "path" : "id"
+ },
+ {
+ "type" : "arg",
+ "type" : "arg",
+ "path" : "time"
+ }
+ ]
+ }
+ },
+ {
+ "arguments" : [
+ {
+ "type" : "variable",
+ "type" : "variable",
+ "path" : "id"
+ },
+ {
+ "type" : "variable",
+ "type" : "variable",
+ "path" : "limit"
+ },
+ {
+ "type" : "variable",
+ "type" : "variable",
+ "path" : "offset"
+ }
+ ],
+ "query" : {
+ "type" : "PagedJdbcQuery",
+ "type" : "PagedJdbcQuery",
+ "sql" : "SELECT *\nFROM \"orders_2\"\nWHERE \"id\" = $1\nORDER BY \"_ingest_time\" DESC NULLS LAST, \"customerid\", \"time\"",
+ "parameters" : [
+ {
+ "type" : "arg",
+ "type" : "arg",
+ "path" : "id"
+ }
+ ]
+ }
+ },
+ {
+ "arguments" : [
+ {
+ "type" : "variable",
+ "type" : "variable",
+ "path" : "customerid"
+ },
+ {
+ "type" : "variable",
+ "type" : "variable",
+ "path" : "time"
+ },
+ {
+ "type" : "variable",
+ "type" : "variable",
+ "path" : "limit"
+ },
+ {
+ "type" : "variable",
+ "type" : "variable",
+ "path" : "offset"
+ }
+ ],
+ "query" : {
+ "type" : "PagedJdbcQuery",
+ "type" : "PagedJdbcQuery",
+ "sql" : "SELECT *\nFROM \"orders_2\"\nWHERE \"customerid\" = $1 AND \"time\" = $2\nORDER BY \"_ingest_time\" DESC NULLS LAST, \"id\"",
+ "parameters" : [
+ {
+ "type" : "arg",
+ "type" : "arg",
+ "path" : "customerid"
+ },
+ {
+ "type" : "arg",
+ "type" : "arg",
+ "path" : "time"
+ }
+ ]
+ }
+ },
+ {
+ "arguments" : [
+ {
+ "type" : "variable",
+ "type" : "variable",
+ "path" : "customerid"
+ },
+ {
+ "type" : "variable",
+ "type" : "variable",
+ "path" : "limit"
+ },
+ {
+ "type" : "variable",
+ "type" : "variable",
+ "path" : "offset"
+ }
+ ],
+ "query" : {
+ "type" : "PagedJdbcQuery",
+ "type" : "PagedJdbcQuery",
+ "sql" : "SELECT *\nFROM \"orders_2\"\nWHERE \"customerid\" = $1\nORDER BY \"_ingest_time\" DESC NULLS LAST, \"id\", \"time\"",
+ "parameters" : [
+ {
+ "type" : "arg",
+ "type" : "arg",
+ "path" : "customerid"
+ }
+ ]
+ }
+ },
+ {
+ "arguments" : [
+ {
+ "type" : "variable",
+ "type" : "variable",
+ "path" : "id"
+ },
+ {
+ "type" : "variable",
+ "type" : "variable",
+ "path" : "customerid"
+ },
+ {
+ "type" : "variable",
+ "type" : "variable",
+ "path" : "time"
+ },
+ {
+ "type" : "variable",
+ "type" : "variable",
+ "path" : "limit"
+ },
+ {
+ "type" : "variable",
+ "type" : "variable",
+ "path" : "offset"
+ }
+ ],
+ "query" : {
+ "type" : "PagedJdbcQuery",
+ "type" : "PagedJdbcQuery",
+ "sql" : "SELECT *\nFROM \"orders_2\"\nWHERE \"id\" = $1 AND \"customerid\" = $2 AND \"time\" = $3\nORDER BY \"_ingest_time\" DESC NULLS LAST",
+ "parameters" : [
+ {
+ "type" : "arg",
+ "type" : "arg",
+ "path" : "id"
+ },
+ {
+ "type" : "arg",
+ "type" : "arg",
+ "path" : "customerid"
+ },
+ {
+ "type" : "arg",
+ "type" : "arg",
+ "path" : "time"
+ }
+ ]
+ }
+ },
+ {
+ "arguments" : [
+ {
+ "type" : "variable",
+ "type" : "variable",
+ "path" : "id"
+ },
+ {
+ "type" : "variable",
+ "type" : "variable",
+ "path" : "customerid"
+ },
+ {
+ "type" : "variable",
+ "type" : "variable",
+ "path" : "limit"
+ },
+ {
+ "type" : "variable",
+ "type" : "variable",
+ "path" : "offset"
+ }
+ ],
+ "query" : {
+ "type" : "PagedJdbcQuery",
+ "type" : "PagedJdbcQuery",
+ "sql" : "SELECT *\nFROM \"orders_2\"\nWHERE \"id\" = $1 AND \"customerid\" = $2\nORDER BY \"_ingest_time\" DESC NULLS LAST, \"time\"",
+ "parameters" : [
+ {
+ "type" : "arg",
+ "type" : "arg",
+ "path" : "id"
+ },
+ {
+ "type" : "arg",
+ "type" : "arg",
+ "path" : "customerid"
+ }
+ ]
+ }
+ }
+ ]
+ }
+ ],
+ "mutations" : [ ],
+ "subscriptions" : [ ],
+ "schema" : {
+ "type" : "string",
+ "type" : "string",
+ "schema" : "\"An RFC-3339 compliant Full Date Scalar\"\nscalar Date\n\n\"An RFC-3339 compliant DateTime Scalar\"\nscalar DateTime\n\n\"An arbitrary precision signed integer\"\nscalar GraphQLBigInteger\n\n\"A JSON scalar\"\nscalar JSON\n\n\"24-hour clock time value string in the format `hh:mm:ss` or `hh:mm:ss.sss`.\"\nscalar LocalTime\n\ntype Orders {\n id: GraphQLBigInteger!\n customerid: GraphQLBigInteger!\n time: DateTime!\n productid: GraphQLBigInteger!\n quantity: GraphQLBigInteger!\n unit_price: Float\n discount: Float\n}\n\ntype Query {\n Orders(id: GraphQLBigInteger, customerid: GraphQLBigInteger, time: DateTime, limit: Int = 10, offset: Int = 0): [Orders!]\n}\n"
+ }
+ }
+}
diff --git a/sqrl-testing/sqrl-integration-tests/src/test/resources/usecases/simple-with-bigint-support/ecommerce/orders.jsonl b/sqrl-testing/sqrl-integration-tests/src/test/resources/usecases/simple-with-bigint-support/ecommerce/orders.jsonl
new file mode 100644
index 0000000000..d30540c413
--- /dev/null
+++ b/sqrl-testing/sqrl-integration-tests/src/test/resources/usecases/simple-with-bigint-support/ecommerce/orders.jsonl
@@ -0,0 +1,14 @@
+{"id": 6, "customerid": 6, "time": "2023-01-02T22:21:00.000Z", "productid": 115,"quantity": 1,"unit_price": 9.22 }
+{"id": 5, "customerid": 11, "time": "2023-01-02T22:21:00.000Z", "productid": 115,"quantity": 1,"unit_price": 9.22 }
+{"id": 2, "customerid": 11, "time": "2023-01-02T22:21:00.000Z", "productid": 115,"quantity": 1,"unit_price": 9.22 }
+{"id": 3, "customerid": 9, "time": "2023-01-02T22:21:00.000Z", "productid": 115,"quantity": 1,"unit_price": 9.22 }
+{"id": 5, "customerid": 11, "time": "2023-01-02T22:22:00.000Z", "productid": 115,"quantity": 1,"unit_price": 9.22 }
+{"id": 2, "customerid": 10, "time": "2023-01-02T22:22:00.000Z", "productid": 115,"quantity": 1,"unit_price": 9.22 }
+{"id": 2, "customerid": 8, "time": "2023-01-02T22:22:00.000Z", "productid": 115,"quantity": 1,"unit_price": 9.22 }
+{"id": 5, "customerid": 11, "time": "2023-01-02T22:24:00.000Z", "productid": 115,"quantity": 2,"unit_price": 9.22 }
+{"id": 2, "customerid": 10, "time": "2023-01-02T22:24:00.000Z", "productid": 115,"quantity": 1,"unit_price": 9.22 }
+{"id": 5, "customerid": 11, "time": "2023-01-02T22:23:00.000Z", "productid": 115,"quantity": 1,"unit_price": 9.22 }
+{"id": 2, "customerid": 11, "time": "2023-01-02T22:23:00.000Z", "productid": 115,"quantity": 1,"unit_price": 9.22 }
+{"id": 5, "customerid": 11, "time": "2023-01-02T22:25:00.000Z", "productid": 115,"quantity": 2,"unit_price": 9.22 }
+{"id": 2, "customerid": 10, "time": "2023-01-02T22:25:00.000Z", "productid": 115,"quantity": 1,"unit_price": 9.22 }
+{"id": 3, "customerid": 7, "time": "2023-01-02T22:25:00.000Z", "productid": 115,"quantity": 1,"unit_price": 9.22 }
\ No newline at end of file
diff --git a/sqrl-testing/sqrl-integration-tests/src/test/resources/usecases/simple-with-bigint-support/ecommerce/orders.schema.yml b/sqrl-testing/sqrl-integration-tests/src/test/resources/usecases/simple-with-bigint-support/ecommerce/orders.schema.yml
new file mode 100644
index 0000000000..f2558f85e7
--- /dev/null
+++ b/sqrl-testing/sqrl-integration-tests/src/test/resources/usecases/simple-with-bigint-support/ecommerce/orders.schema.yml
@@ -0,0 +1,29 @@
+---
+name: "Orders"
+schema_version: "1"
+partial_schema: false
+columns:
+- name: "id"
+ type: "BIGINT"
+ tests:
+ - "not_null"
+- name: "customerid"
+ type: "BIGINT"
+ tests:
+ - "not_null"
+- name: "time"
+ type: "TIMESTAMP"
+ tests:
+ - "not_null"
+- name: "productid"
+ type: "BIGINT"
+ tests:
+ - "not_null"
+- name: "quantity"
+ type: "BIGINT"
+ tests:
+ - "not_null"
+- name: "unit_price"
+ type: "DOUBLE"
+- name: "discount"
+ type: "DOUBLE"
diff --git a/sqrl-testing/sqrl-integration-tests/src/test/resources/usecases/simple-with-bigint-support/ecommerce/orders.table.json b/sqrl-testing/sqrl-integration-tests/src/test/resources/usecases/simple-with-bigint-support/ecommerce/orders.table.json
new file mode 100644
index 0000000000..c6ddeda039
--- /dev/null
+++ b/sqrl-testing/sqrl-integration-tests/src/test/resources/usecases/simple-with-bigint-support/ecommerce/orders.table.json
@@ -0,0 +1,19 @@
+{
+ "version": 1,
+ "flink" : {
+ "format" : "flexible-json",
+ "path" : "${DATA_PATH}/orders.jsonl",
+ "source.monitor-interval" : 10000,
+ "connector" : "filesystem"
+ },
+ "table" : {
+ "type" : "source",
+ "timestamp" : "_ingest_time",
+ "primary-key" : ["id", "customerid", "time"]
+ },
+ "metadata" : {
+ "_ingest_time" : {
+ "attribute" : "proctime()"
+ }
+ }
+}
\ No newline at end of file
diff --git a/sqrl-testing/sqrl-integration-tests/src/test/resources/usecases/simple-with-bigint-support/package.json b/sqrl-testing/sqrl-integration-tests/src/test/resources/usecases/simple-with-bigint-support/package.json
new file mode 100644
index 0000000000..7169fb3df5
--- /dev/null
+++ b/sqrl-testing/sqrl-integration-tests/src/test/resources/usecases/simple-with-bigint-support/package.json
@@ -0,0 +1,6 @@
+{
+ "version": "1",
+ "compiler": {
+ "extendedScalarTypes": true
+ }
+}
\ No newline at end of file
diff --git a/sqrl-testing/sqrl-integration-tests/src/test/resources/usecases/simple-with-bigint-support/simple-with-bigint-support.graphql b/sqrl-testing/sqrl-integration-tests/src/test/resources/usecases/simple-with-bigint-support/simple-with-bigint-support.graphql
new file mode 100644
index 0000000000..b261100dd2
--- /dev/null
+++ b/sqrl-testing/sqrl-integration-tests/src/test/resources/usecases/simple-with-bigint-support/simple-with-bigint-support.graphql
@@ -0,0 +1,28 @@
+"An RFC-3339 compliant Full Date Scalar"
+scalar Date
+
+"An RFC-3339 compliant DateTime Scalar"
+scalar DateTime
+
+"An arbitrary precision signed integer"
+scalar GraphQLBigInteger
+
+"A JSON scalar"
+scalar JSON
+
+"24-hour clock time value string in the format `hh:mm:ss` or `hh:mm:ss.sss`."
+scalar LocalTime
+
+type Orders {
+ id: GraphQLBigInteger!
+ customerid: GraphQLBigInteger!
+ time: DateTime!
+ productid: GraphQLBigInteger!
+ quantity: GraphQLBigInteger!
+ unit_price: Float
+ discount: Float
+}
+
+type Query {
+ Orders(id: GraphQLBigInteger, customerid: GraphQLBigInteger, time: DateTime, limit: Int = 10, offset: Int = 0): [Orders!]
+}
\ No newline at end of file
diff --git a/sqrl-testing/sqrl-integration-tests/src/test/resources/usecases/simple-with-bigint-support/simple-with-bigint-support.sqrl b/sqrl-testing/sqrl-integration-tests/src/test/resources/usecases/simple-with-bigint-support/simple-with-bigint-support.sqrl
new file mode 100644
index 0000000000..d5c12f0907
--- /dev/null
+++ b/sqrl-testing/sqrl-integration-tests/src/test/resources/usecases/simple-with-bigint-support/simple-with-bigint-support.sqrl
@@ -0,0 +1,4 @@
+IMPORT ecommerce.Orders;
+
+/*+test */
+BigIntOrdersTest := SELECT * FROM Orders ORDER BY id ASC;
\ No newline at end of file
diff --git a/sqrl-testing/sqrl-integration-tests/src/test/resources/usecases/simple-with-bigint-support/snapshots-simple-with-bigint-support/BigIntOrdersTest.snapshot b/sqrl-testing/sqrl-integration-tests/src/test/resources/usecases/simple-with-bigint-support/snapshots-simple-with-bigint-support/BigIntOrdersTest.snapshot
new file mode 100644
index 0000000000..72047125f9
--- /dev/null
+++ b/sqrl-testing/sqrl-integration-tests/src/test/resources/usecases/simple-with-bigint-support/snapshots-simple-with-bigint-support/BigIntOrdersTest.snapshot
@@ -0,0 +1 @@
+{"data":{"BigIntOrdersTest":[{"id":2,"customerid":8,"time":"2023-01-03T06:22:00.000Z","productid":115,"quantity":1,"unit_price":9.22,"discount":null},{"id":2,"customerid":10,"time":"2023-01-03T06:22:00.000Z","productid":115,"quantity":1,"unit_price":9.22,"discount":null},{"id":2,"customerid":10,"time":"2023-01-03T06:24:00.000Z","productid":115,"quantity":1,"unit_price":9.22,"discount":null},{"id":2,"customerid":10,"time":"2023-01-03T06:25:00.000Z","productid":115,"quantity":1,"unit_price":9.22,"discount":null},{"id":2,"customerid":11,"time":"2023-01-03T06:21:00.000Z","productid":115,"quantity":1,"unit_price":9.22,"discount":null},{"id":2,"customerid":11,"time":"2023-01-03T06:23:00.000Z","productid":115,"quantity":1,"unit_price":9.22,"discount":null},{"id":3,"customerid":7,"time":"2023-01-03T06:25:00.000Z","productid":115,"quantity":1,"unit_price":9.22,"discount":null},{"id":3,"customerid":9,"time":"2023-01-03T06:21:00.000Z","productid":115,"quantity":1,"unit_price":9.22,"discount":null},{"id":5,"customerid":11,"time":"2023-01-03T06:21:00.000Z","productid":115,"quantity":1,"unit_price":9.22,"discount":null},{"id":5,"customerid":11,"time":"2023-01-03T06:22:00.000Z","productid":115,"quantity":1,"unit_price":9.22,"discount":null}]}}
\ No newline at end of file
diff --git a/sqrl-tools/sqrl-config/src/main/java/com/datasqrl/config/CompilerConfigImpl.java b/sqrl-tools/sqrl-config/src/main/java/com/datasqrl/config/CompilerConfigImpl.java
index 3270e13a81..949f0a7cd2 100644
--- a/sqrl-tools/sqrl-config/src/main/java/com/datasqrl/config/CompilerConfigImpl.java
+++ b/sqrl-tools/sqrl-config/src/main/java/com/datasqrl/config/CompilerConfigImpl.java
@@ -21,6 +21,10 @@ public boolean isAddArguments() {
return sqrlConfig.asBool("addArguments")
.getOptional().orElse(true);
}
+ public boolean isExtendedScalarTypes() {
+ return sqrlConfig.asBool("extendedScalarTypes")
+ .getOptional().orElse(false); // by default don't use the extended scalar types (map PK as float) for backward compatibility
+ }
@Override
public String getLogger() {
diff --git a/sqrl-tools/sqrl-config/src/main/resources/jsonSchema/packageSchema.json b/sqrl-tools/sqrl-config/src/main/resources/jsonSchema/packageSchema.json
index aaab440ccc..56a127045b 100644
--- a/sqrl-tools/sqrl-config/src/main/resources/jsonSchema/packageSchema.json
+++ b/sqrl-tools/sqrl-config/src/main/resources/jsonSchema/packageSchema.json
@@ -76,6 +76,9 @@
},
"logger": {
"type": "string"
+ },
+ "extendedScalarTypes": {
+ "type": "boolean"
}
}
},
diff --git a/sqrl-tools/sqrl-packager/src/main/java/com/datasqrl/compile/CompilationProcessV2.java b/sqrl-tools/sqrl-packager/src/main/java/com/datasqrl/compile/CompilationProcessV2.java
index a3520be7bd..45af50a7a5 100644
--- a/sqrl-tools/sqrl-packager/src/main/java/com/datasqrl/compile/CompilationProcessV2.java
+++ b/sqrl-tools/sqrl-packager/src/main/java/com/datasqrl/compile/CompilationProcessV2.java
@@ -2,7 +2,6 @@
import com.datasqrl.actions.CreateDatabaseQueries;
import com.datasqrl.actions.GraphqlPostplanHook;
-import com.datasqrl.actions.InferGraphqlSchema;
import com.datasqrl.actions.DagWriter;
import com.datasqrl.canonicalizer.Name;
import com.datasqrl.config.BuildPath;
@@ -16,6 +15,7 @@
import com.datasqrl.plan.queries.APISource;
import com.datasqrl.plan.queries.APISourceImpl;
import com.datasqrl.util.ServiceLoaderDiscovery;
+import com.datasqrl.v2.graphql.InferGraphqlSchema2;
import com.datasqrl.v2.dag.DAGBuilder;
import com.datasqrl.v2.dag.DAGPlanner;
import com.datasqrl.v2.dag.PipelineDAG;
@@ -45,7 +45,7 @@ public class CompilationProcessV2 {
private final PhysicalPlanner physicalPlanner;
private final GraphqlPostplanHook graphqlPostplanHook;
private final CreateDatabaseQueries createDatabaseQueries;
- private final InferGraphqlSchema inferencePostcompileHook;
+ private final InferGraphqlSchema2 inferGraphqlSchema;
private final DagWriter writeDeploymentArtifactsHook;
// private final FlinkSqlGenerator flinkSqlGenerator;
private final GraphqlSourceFactory graphqlSourceFactory;
@@ -77,17 +77,14 @@ public Pair executeCompilation(Optional testsPath)
- make sure we generate the right testplan
- create the RootGraphQL model and attach to serverPlan
*/
- if (serverPlan.isPresent() && false) {
+ if (serverPlan.isPresent()) {
Optional apiSource = graphqlSourceFactory.get();
if (apiSource.isEmpty() || executionGoal == ExecutionGoal.TEST) { //Infer schema from functions
- //TODO: rewrite the following to use the functions from the serverPlan
- apiSource = inferencePostcompileHook.inferGraphQLSchema()
+ apiSource = inferGraphqlSchema.inferGraphQLSchema(serverPlan.get())
.map(schemaString -> new APISourceImpl(Name.system(""), schemaString));
}
assert apiSource.isPresent();
-
- //TODO: Validates and generates queries
- inferencePostcompileHook.validateAndGenerateQueries(apiSource.get(), null);
+ inferGraphqlSchema.validateAndGenerateQueries(apiSource.get());
//TODO: Generates RootGraphQLModel, use serverplan as argument only
graphqlPostplanHook.updatePlan(apiSource, null);