text: #include "common/exception.hpp"
  #include "parser/query_node/select_node.hpp"
  #include "parser/query_node/set_operation_node.hpp"
  #include "parser/statement/select_statement.hpp"
  #include "parser/transformer.hpp"
  
  using namespace duckdb;
  using namespace postgres;
  using namespace std;
  
  unique_ptr<QueryNode> Transformer::TransformSelectNode(postgres::SelectStmt *stmt) {
  	unique_ptr<QueryNode> node;
  	switch (stmt->op) {
  	case SETOP_NONE: {
  		node = make_unique<SelectNode>();
  		auto result = (SelectNode *)node.get();
  		if (stmt->valuesLists) {
  			TransformValuesList(stmt->valuesLists, result->values);
  			return node;
  		} else if (!stmt->targetList) {
  			throw ParserException("SELECT clause without selection list");
  		}
  		// checks distinct clause
  		if (stmt->distinctClause != NULL) {
  			result->select_distinct = true;
  			// checks distinct on clause
  			auto target = reinterpret_cast<Node *>(stmt->distinctClause->head->data.ptr_value);
  			if (target) {
  				//  add the columns defined in the ON clause to the select list
  				if (!TransformExpressionList(stmt->distinctClause, result->distinct_on_targets)) {
  					throw Exception("Failed to transform expression list from DISTINCT ON.");
  				}
  			}
  		}
  		// from table
  		result->from_table = TransformFrom(stmt->fromClause);
  		// group by
  		TransformGroupBy(stmt->groupClause, result->groups);
  		result->having = TransformExpression(stmt->havingClause);
  		if (result->groups.size() == 0 && result->having) {
  			throw ParserException("a GROUP BY clause is required before HAVING");
  		}
  		// where
  		result->where_clause = TransformExpression(stmt->whereClause);
  		// select list
  		if (!TransformExpressionList(stmt->targetList, result->select_list)) {
  			throw Exception("Failed to transform expression list.");
  		}
  		break;
  	}
  	case SETOP_UNION:
  	case SETOP_EXCEPT:
  	case SETOP_INTERSECT: {
  		node = make_unique<SetOperationNode>();
  		auto result = (SetOperationNode *)node.get();
  		result->left = TransformSelectNode(stmt->larg);
  		result->right = TransformSelectNode(stmt->rarg);
  		if (!result->left || !result->right) {
  			throw Exception("Failed to transform setop children.");
  		}
  
  		result->select_distinct = true;
  		switch (stmt->op) {
  		case SETOP_UNION:
  			result->select_distinct = !stmt->all;
  			result->setop_type = SetOperationType::UNION;
  			break;
  		case SETOP_EXCEPT:
  			result->setop_type = SetOperationType::EXCEPT;
  			break;
  		case SETOP_INTERSECT:
  			result->setop_type = SetOperationType::INTERSECT;
  			break;
  		default:
  			throw Exception("Unexpected setop type");
  		}
  		// if we compute the distinct result here, we do not have to do this in
  		// the children. This saves a bunch of unnecessary DISTINCTs.
  		if (result->select_distinct) {
  			result->left->select_distinct = false;
  			result->right->select_distinct = false;
  		}
  		break;
  	}
  	default:
  		throw NotImplementedException("Statement type %d not implemented!", stmt->op);
  	}
  	// transform the common properties
  	// both the set operations and the regular select can have an ORDER BY/LIMIT attached to them
  	TransformOrderBy(stmt->sortClause, node->orders);
  	if (stmt->limitCount) {
  		node->limit = TransformExpression(stmt->limitCount);
  	}
  	if (stmt->limitOffset) {
  		node->offset = TransformExpression(stmt->limitOffset);
  	}
  	return node;
  }
  