src/query-ast-to-sql-ast/index.js
function handleUnionSelections(
  sqlASTNode,
  children,
  selections,
  gqlType,
  namespace,
  depth,
  options,
  context,
  internalOptions = {}
) {
  for (let selection of selections) {
    // we need to figure out what kind of selection this is
    switch (selection.kind) {
    case 'Field':
      // has this field been requested once already? GraphQL does not protect against duplicates so we have to check for it
      const existingNode = children.find(child => child.fieldName === selection.name.value && child.type === 'table')
      let newNode = new SQLASTNode(sqlASTNode)
      if (existingNode) {
        newNode = existingNode
      } else {
        children.push(newNode)
      }
      if (internalOptions.defferedFrom) {
        newNode.defferedFrom = internalOptions.defferedFrom
      }
      populateASTNode.call(this, selection, gqlType, newNode, namespace, depth + 1, options, context)
      break
    // if its an inline fragment, it has some fields and we gotta recurse thru all them
    case 'InlineFragment':
      {
        const selectionNameOfType = selection.typeCondition.name.value
        // normally, we would scan for the extra join-monster data on the current gqlType.
        // but the gqlType is the Union. The data isn't there, its on each of the types that make up the union
        // lets find that type and handle the selections based on THAT type instead
        const deferredType = this.schema._typeMap[selectionNameOfType]
        const deferToObjectType = deferredType.constructor.name === 'GraphQLObjectType'
        const handler = deferToObjectType ? handleSelections : handleUnionSelections
        if (deferToObjectType) {
          const typedChildren = sqlASTNode.typedChildren
          children = typedChildren[deferredType.name] = typedChildren[deferredType.name] || []
          internalOptions.defferedFrom = gqlType
        }
        handler.call(
          this, sqlASTNode, children,
          selection.selectionSet.selections, deferredType, namespace,
          depth, options, context,
          internalOptions
        )
      }
      break
    // if its a named fragment, we need to grab the fragment definition by its name and recurse over those fields
    case 'FragmentSpread':
      {
        const fragmentName = selection.name.value
        const fragment = this.fragments[fragmentName]
        const fragmentNameOfType = fragment.typeCondition.name.value
        const deferredType = this.schema._typeMap[fragmentNameOfType]
        const deferToObjectType = deferredType.constructor.name === 'GraphQLObjectType'
        const handler = deferToObjectType ? handleSelections : handleUnionSelections
        if (deferToObjectType) {
          const typedChildren = sqlASTNode.typedChildren
          children = typedChildren[deferredType.name] = typedChildren[deferredType.name] || []
          internalOptions.defferedFrom = gqlType
        }
        handler.call(
          this, sqlASTNode, children,
          fragment.selectionSet.selections, deferredType, namespace,
          depth, options, context,
          internalOptions
        )
      }
      break
    /* istanbul ignore next */
    default:
      throw new Error('Unknown selection kind: ' + selection.kind)
    }
  }
}
function handleUnionSelections(
  sqlASTNode,
  children,
  selections,
  gqlType,
  namespace,
  depth,
  options,
  context,
  internalOptions = {}
) {
  for (let selection of selections) {
    // we need to figure out what kind of selection this is
    switch (selection.kind) {
    case 'Field':
      // has this field been requested once already? GraphQL does not protect against duplicates so we have to check for it
      const existingNode = children.find(child => child.fieldName === selection.name.value && child.type === 'table')
      let newNode = new SQLASTNode(sqlASTNode)
      if (existingNode) {
        newNode = existingNode
      } else {
        children.push(newNode)
      }
      if (internalOptions.defferedFrom) {
        newNode.defferedFrom = internalOptions.defferedFrom
      }
      populateASTNode.call(this, selection, gqlType, newNode, namespace, depth + 1, options, context)
      break
    // if its an inline fragment, it has some fields and we gotta recurse thru all them
    case 'InlineFragment':
      {
        const selectionNameOfType = selection.typeCondition.name.value
        // normally, we would scan for the extra join-monster data on the current gqlType.
        // but the gqlType is the Union. The data isn't there, its on each of the types that make up the union
        // lets find that type and handle the selections based on THAT type instead
        const deferredType = this.schema._typeMap[selectionNameOfType]
        const deferToObjectType = deferredType.constructor.name === 'GraphQLObjectType'
        const handler = deferToObjectType ? handleSelections : handleUnionSelections
        if (deferToObjectType) {
          const typedChildren = sqlASTNode.typedChildren
          children = typedChildren[deferredType.name] = typedChildren[deferredType.name] || []
          internalOptions.defferedFrom = gqlType
        }
        handler.call(
          this, sqlASTNode, children,
          selection.selectionSet.selections, deferredType, namespace,
          depth, options, context,
          internalOptions
        )
      }
      break
    // if its a named fragment, we need to grab the fragment definition by its name and recurse over those fields
    case 'FragmentSpread':
      {
        const fragmentName = selection.name.value
        const fragment = this.fragments[fragmentName]
        const fragmentNameOfType = fragment.typeCondition.name.value
        const deferredType = this.schema._typeMap[fragmentNameOfType]
        const deferToObjectType = deferredType.constructor.name === 'GraphQLObjectType'
        const handler = deferToObjectType ? handleSelections : handleUnionSelections
        if (deferToObjectType) {
          const typedChildren = sqlASTNode.typedChildren
          children = typedChildren[deferredType.name] = typedChildren[deferredType.name] || []
          internalOptions.defferedFrom = gqlType
        }
        handler.call(
          this, sqlASTNode, children,
          fragment.selectionSet.selections, deferredType, namespace,
          depth, options, context,
          internalOptions
        )
      }
      break
    /* istanbul ignore next */
    default:
      throw new Error('Unknown selection kind: ' + selection.kind)
    }
  }
}
function handleUnionSelections ( sqlASTNode , children , selections , gqlType , namespace , depth , options , context , internalOptions = { } ) { for ( let selection of selections ) { // we need to figure out what kind of selection this is switch ( selection . kind ) { case 'Field' : // has this field been requested once already? GraphQL does not protect against duplicates so we have to check for it const existingNode = children . find ( child => child . fieldName === selection . name . value && child . type === 'table' ) let newNode = new SQLASTNode ( sqlASTNode ) if ( existingNode ) { newNode = existingNode } else { children . push ( newNode ) } if ( internalOptions . defferedFrom ) { newNode . defferedFrom = internalOptions . defferedFrom } populateASTNode . call ( this , selection , gqlType , newNode , namespace , depth + 1 , options , context ) break // if its an inline fragment, it has some fields and we gotta recurse thru all them case 'InlineFragment' : { const selectionNameOfType = selection . typeCondition . name . value // normally, we would scan for the extra join-monster data on the current gqlType. // but the gqlType is the Union. The data isn't there, its on each of the types that make up the union // lets find that type and handle the selections based on THAT type instead const deferredType = this . schema . _typeMap [ selectionNameOfType ] const deferToObjectType = deferredType . constructor . name === 'GraphQLObjectType' const handler = deferToObjectType ? handleSelections : handleUnionSelections if ( deferToObjectType ) { const typedChildren = sqlASTNode . typedChildren children = typedChildren [ deferredType . name ] = typedChildren [ deferredType . name ] || [ ] internalOptions . defferedFrom = gqlType } handler . call ( this , sqlASTNode , children , selection . selectionSet . selections , deferredType , namespace , depth , options , context , internalOptions ) } break // if its a named fragment, we need to grab the fragment definition by its name and recurse over those fields case 'FragmentSpread' : { const fragmentName = selection . name . value const fragment = this . fragments [ fragmentName ] const fragmentNameOfType = fragment . typeCondition . name . value const deferredType = this . schema . _typeMap [ fragmentNameOfType ] const deferToObjectType = deferredType . constructor . name === 'GraphQLObjectType' const handler = deferToObjectType ? handleSelections : handleUnionSelections if ( deferToObjectType ) { const typedChildren = sqlASTNode . typedChildren children = typedChildren [ deferredType . name ] = typedChildren [ deferredType . name ] || [ ] internalOptions . defferedFrom = gqlType } handler . call ( this , sqlASTNode , children , fragment . selectionSet . selections , deferredType , namespace , depth , options , context , internalOptions ) } break /* istanbul ignore next */ default : throw new Error ( 'Unknown selection kind: ' + selection . kind ) } } }
we need to collect all fields from all the fragments requested in the union type and ask for them in SQL
we need to collect all fields from all the fragments requested in the union type and ask for them in SQL