private SqlQueryStructure()

in src/Core/Resolvers/Sql Query Structures/SqlQueryStructure.cs [369:534]


        private SqlQueryStructure(
                IMiddlewareContext ctx,
                IDictionary<string, object?> queryParams,
                ISqlMetadataProvider sqlMetadataProvider,
                IAuthorizationResolver authorizationResolver,
                IObjectField schemaField,
                FieldNode? queryField,
                IncrementingInteger counter,
                RuntimeConfigProvider runtimeConfigProvider,
                GQLFilterParser gQLFilterParser,
                string entityName = "")
            : this(sqlMetadataProvider,
                  authorizationResolver,
                  gQLFilterParser,
                  predicates: null,
                  entityName: entityName,
                  counter: counter
                  )
        {
            _ctx = ctx;
            IOutputType outputType = schemaField.Type;
            _underlyingFieldType = GraphQLUtils.UnderlyingGraphQLEntityType(outputType);

            // extract the query argument schemas before switching schemaField to point to *Connetion.items
            // since the pagination arguments are not placed on the items, but on the pagination query
            IFieldCollection<IInputField> queryArgumentSchemas = schemaField.Arguments;

            PaginationMetadata.IsPaginated = QueryBuilder.IsPaginationType(_underlyingFieldType);

            if (PaginationMetadata.IsPaginated)
            {
                if (queryField != null && queryField.SelectionSet != null)
                {
                    // process pagination fields without overriding them
                    ProcessPaginationFields(queryField.SelectionSet.Selections);

                    // override schemaField and queryField with the schemaField and queryField of *Connection.items
                    queryField = ExtractQueryField(queryField);
                }

                schemaField = ExtractItemsSchemaField(schemaField);

                outputType = schemaField.Type;
                _underlyingFieldType = GraphQLUtils.UnderlyingGraphQLEntityType(outputType);

                // this is required to correctly keep track of which pagination metadata
                // refers to what section of the json
                // for a paginationless chain:
                //      getbooks > publisher > books > publisher
                //      each new entry in the chain corresponds to a subquery so there will be
                //      a matching pagination metadata object chain
                // for a chain with pagination:
                //      books > items > publisher > books > publisher
                //      items do not have a matching subquery so the line of code below is
                //      required to build a pagination metadata chain matching the json result
                PaginationMetadata.Subqueries.Add(QueryBuilder.PAGINATION_FIELD_NAME, PaginationMetadata.MakeEmptyPaginationMetadata());
            }

            EntityName = sqlMetadataProvider.GetDatabaseType() == DatabaseType.DWSQL ? GraphQLUtils.GetEntityNameFromContext(ctx) : _underlyingFieldType.Name;
            bool isGroupByQuery = queryField?.Name.Value == QueryBuilder.GROUP_BY_FIELD_NAME;

            if (GraphQLUtils.TryExtractGraphQLFieldModelName(_underlyingFieldType.Directives, out string? modelName))
            {
                EntityName = modelName;
            }

            DatabaseObject.SchemaName = sqlMetadataProvider.GetSchemaName(EntityName);
            DatabaseObject.Name = sqlMetadataProvider.GetDatabaseObjectName(EntityName);
            SourceAlias = CreateTableAlias();

            // SelectionSet will not be null when a field is not a leaf.
            // There may be another entity to resolve as a sub-query.
            if (queryField != null && queryField.SelectionSet != null)
            {
                if (isGroupByQuery)
                {
                    ProcessGroupByField(queryField, ctx);
                }
                else
                {
                    AddGraphQLFields(queryField.SelectionSet.Selections, runtimeConfigProvider);
                }
            }

            HttpContext httpContext = GraphQLFilterParser.GetHttpContextFromMiddlewareContext(ctx);
            // Process Authorization Policy of the entity being processed.
            AuthorizationPolicyHelpers.ProcessAuthorizationPolicies(EntityActionOperation.Read, queryStructure: this, httpContext, authorizationResolver, sqlMetadataProvider);

            if (outputType.IsNonNullType())
            {
                IsListQuery = outputType.InnerType().IsListType();
            }
            else
            {
                IsListQuery = outputType.IsListType();
            }

            if (IsListQuery)
            {
                runtimeConfigProvider.TryGetConfig(out RuntimeConfig? runtimeConfig);
                if (queryParams.ContainsKey(QueryBuilder.PAGE_START_ARGUMENT_NAME))
                {
                    // parse first parameter for all list queries
                    object? firstObject = queryParams[QueryBuilder.PAGE_START_ARGUMENT_NAME];
                    _limit = runtimeConfig?.GetPaginationLimit((int?)firstObject);
                }
                else
                {
                    // if first is not passed, we should use the default page size.
                    _limit = runtimeConfig?.DefaultPageSize();
                }
            }

            if (IsListQuery && queryParams.ContainsKey(QueryBuilder.FILTER_FIELD_NAME))
            {
                object? filterObject = queryParams[QueryBuilder.FILTER_FIELD_NAME];

                if (filterObject is not null)
                {
                    List<ObjectFieldNode> filterFields = (List<ObjectFieldNode>)filterObject;
                    Predicates.Add(GraphQLFilterParser.Parse(
                                        _ctx,
                                        filterArgumentSchema: queryArgumentSchemas[QueryBuilder.FILTER_FIELD_NAME],
                                        fields: filterFields,
                                        queryStructure: this));
                }
            }

            // primary key should only be added to order by for non groupby queries.
            OrderByColumns = isGroupByQuery ? [] : PrimaryKeyAsOrderByColumns();
            if (IsListQuery && queryParams.ContainsKey(QueryBuilder.ORDER_BY_FIELD_NAME))
            {
                object? orderByObject = queryParams[QueryBuilder.ORDER_BY_FIELD_NAME];

                if (orderByObject is not null)
                {
                    OrderByColumns = ProcessGqlOrderByArg((List<ObjectFieldNode>)orderByObject, queryArgumentSchemas[QueryBuilder.ORDER_BY_FIELD_NAME], isGroupByQuery);
                }
            }

            // need to run after the rest of the query has been processed since it relies on
            // TableName, SourceAlias, Columns, and _limit
            if (PaginationMetadata.IsPaginated)
            {
                AddPaginationPredicate(SqlPaginationUtil.ParseAfterFromQueryParams(queryParams, PaginationMetadata, sqlMetadataProvider, EntityName, runtimeConfigProvider));

                if (PaginationMetadata.RequestedEndCursor)
                {
                    AddColumnsForEndCursor(isGroupByQuery);
                }

                if (PaginationMetadata.RequestedHasNextPage || PaginationMetadata.RequestedEndCursor)
                {
                    _limit++;
                }
            }

            // If there are no columns, add the primary key column
            // to prevent failures when executing the database query.
            if (!Columns.Any() && !isGroupByQuery)
            {
                AddColumn(PrimaryKey()[0]);
            }

            ParametrizeColumns();
        }