function arrToConnection(data, sqlAST) {
  // use "post-order" tree traversal
  for (let astChild of sqlAST.children || []) {
    if (Array.isArray(data)) {
      for (let dataItem of data) {
        recurseOnObjInData(dataItem, astChild)
      }
    } else if (data) {
      recurseOnObjInData(data, astChild)
    }
  }
  const pageInfo = {
    hasNextPage: false,
    hasPreviousPage: false
  }
  if (!data) {
    if (sqlAST.paginate) {
      return {
        pageInfo,
        edges: []
      }
    }
    return null
  }
  // is cases where pagination was done, take the data and convert to the connection object
  // if any two fields happen to become a reference to the same object (when their `uniqueKey`s are the same),
  // we must prevent the recursive processing from visting the same object twice, because mutating the object the first
  // time changes it everywhere. we'll set the `_paginated` property to true to prevent this
  if (sqlAST.paginate && !data._paginated) {
    if (sqlAST.sortKey || idx(sqlAST, _ => _.junction.sortKey)) {
      if (idx(sqlAST, _ => _.args.first)) {
        // we fetched an extra one in order to determine if there is a next page, if there is one, pop off that extra
        if (data.length > sqlAST.args.first) {
          pageInfo.hasNextPage = true
          data.pop()
        }
      } else if (sqlAST.args && sqlAST.args.last) {
        // if backward paging, do the same, but also reverse it
        if (data.length > sqlAST.args.last) {
          pageInfo.hasPreviousPage = true
          data.pop()
        }
        data.reverse()
      }
      // convert nodes to edges and compute the cursor for each
      // TODO: only compute all the cursor if asked for them
      const sortKey = sqlAST.sortKey || sqlAST.junction.sortKey
      const edges = data.map(obj => {
        const cursor = {}
        const key = sortKey.key
        for (let column of wrap(key)) {
          cursor[column] = obj[column]
        }
        return { cursor: objToCursor(cursor), node: obj }
      })
      if (data.length) {
        pageInfo.startCursor = edges[0].cursor
        pageInfo.endCursor = last(edges).cursor
      }
      return { edges, pageInfo, _paginated: true }
    }
    if (sqlAST.orderBy || (sqlAST.junction && sqlAST.junction.orderBy)) {
      let offset = 0
      if (idx(sqlAST, _ => _.args.after)) {
        offset = cursorToOffset(sqlAST.args.after) + 1
      }
      // $total was a special column for determining the total number of items
      const arrayLength = data[0] && parseInt(data[0].$total, 10)
      const connection = connectionFromArraySlice(data, sqlAST.args || {}, { sliceStart: offset, arrayLength })
      connection.total = arrayLength || 0
      connection._paginated = true
      return connection
    }
  }
  return data
}