/***************************************************************************
                               qgssqlstatement.h
                             ---------------------
    begin                : April 2016
    copyright            : (C) 2011 by Martin Dobias
    copyright            : (C) 2016 by Even Rouault
    email                : even.rouault at spatialys.com
 ***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#ifndef QGSSQLSTATEMENT_H
#define QGSSQLSTATEMENT_H

#include <memory>

#include "qgis_core.h"
#include "qgis_sip.h"

#include <QCoreApplication>
#include <QList>
#include <QMetaType>
#include <QSet>
#include <QStringList>
#include <QVariant>

/**
 * \ingroup core
 * \brief Parses SQL statements.
 */
class CORE_EXPORT QgsSQLStatement
{
    Q_DECLARE_TR_FUNCTIONS( QgsSQLStatement )
  public:
    /**
     * Creates a new statement based on the provided string.
     */
    QgsSQLStatement( const QString &statement );

    QgsSQLStatement( const QgsSQLStatement &other );

    QgsSQLStatement &operator=( const QgsSQLStatement &other );
    virtual ~QgsSQLStatement();

    //! Returns TRUE if an error occurred when parsing the input statement
    bool hasParserError() const;
    //! Returns parser error
    QString parserErrorString() const;

    /**
     * Performs basic validity checks. Basically checking that columns referencing
     * a table, references a specified table. Returns TRUE if the validation is
     * successful
    */
    bool doBasicValidationChecks( QString &errorMsgOut SIP_OUT ) const;

    class Node;

    /**
     * Returns the root node of the statement.
     * The root node is NULLPTR if parsing has failed.
     */
    const QgsSQLStatement::Node *rootNode() const;

    /**
     * Returns the original, unmodified statement string.
     * If there was none supplied because it was constructed by sole
     * API calls, dump() will be used to create one instead.
     */
    QString statement() const;

    /**
     * Returns the statement string, constructed from the internal
     * abstract syntax tree. This does not contain any nice whitespace
     * formatting or comments. In general it is preferable to use
     * statement() instead.
     */
    QString dump() const;

    /**
     * Returns a quoted column reference (in double quotes)
     * \see quotedString()
     * \see quotedIdentifierIfNeeded()
     */
    static QString quotedIdentifier( QString name );

    /**
     * Returns a quoted column reference (in double quotes) if needed, or
     * otherwise the original string.
     * \see quotedString()
     * \see quotedIdentifier()
     */
    static QString quotedIdentifierIfNeeded( const QString &name );

    /**
     * Remove double quotes from an identifier.
     * \see quotedIdentifier()
     */
    static QString stripQuotedIdentifier( QString text );

    /**
     * Remove double quotes from an Microsoft style identifier.
     * \see quotedIdentifier()
     */
    static QString stripMsQuotedIdentifier( QString text );

    /**
     * Returns a quoted version of a string (in single quotes)
     * \see quotedIdentifier()
     * \see quotedIdentifierIfNeeded()
     */
    static QString quotedString( QString text );

    /**
     * \brief list of unary operators
     * \note if any change is made here, the definition of QgsSQLStatement::UnaryOperatorText[] must be adapted.
     */
    enum UnaryOperator
    {
      uoNot,
      uoMinus,
    };

    /**
     * \brief list of binary operators
     * \note if any change is made here, the definition of QgsSQLStatement::BinaryOperatorText[] must be adapted.
     */
    enum BinaryOperator
    {
      // logical
      boOr,
      boAnd,

      // comparison
      boEQ, // =
      boNE, // <>
      boLE, // <=
      boGE, // >=
      boLT, // <
      boGT, // >
      boLike,
      boNotLike,
      boILike,
      boNotILike,
      boIs,
      boIsNot,

      // math
      boPlus,
      boMinus,
      boMul,
      boDiv,
      boIntDiv,
      boMod,
      boPow,

      // strings
      boConcat,
    };

    /**
     * \brief list of join types
     * \note if any change is made here, the definition of QgsSQLStatement::JoinTypeText[] must be adapted.
     */
    enum JoinType
    {
      jtDefault,
      jtLeft,
      jtLeftOuter,
      jtRight,
      jtRightOuter,
      jtCross,
      jtInner,
      jtFull
    };

    //! \note not available in Python bindings
    static const char *BINARY_OPERATOR_TEXT[] SIP_SKIP;

    //! \note not available in Python bindings
    static const char *UNARY_OPERATOR_TEXT[] SIP_SKIP;

    //! \note not available in Python bindings
    static const char *JOIN_TYPE_TEXT[] SIP_SKIP;

    //////

    class Visitor; // visitor interface is defined below

    //! Node type
    enum NodeType
    {
      ntUnaryOperator,
      ntBinaryOperator,
      ntInOperator,
      ntBetweenOperator,
      ntFunction,
      ntLiteral,
      ntColumnRef,
      ntSelectedColumn,
      ntSelect,
      ntTableDef,
      ntJoin,
      ntColumnSorted,
      ntCast
    };

    /**
     * \ingroup core
     * \brief Abstract node class for SQL statement nodes.
    */
    class CORE_EXPORT Node
    {
#ifdef SIP_RUN
        SIP_CONVERT_TO_SUBCLASS_CODE
        switch ( sipCpp->nodeType() )
        {
          case QgsSQLStatement::ntUnaryOperator:
            sipType = sipType_QgsSQLStatement_NodeUnaryOperator;
            break;
          case QgsSQLStatement::ntBinaryOperator:
            sipType = sipType_QgsSQLStatement_NodeBinaryOperator;
            break;
          case QgsSQLStatement::ntInOperator:
            sipType = sipType_QgsSQLStatement_NodeInOperator;
            break;
          case QgsSQLStatement::ntBetweenOperator:
            sipType = sipType_QgsSQLStatement_NodeBetweenOperator;
            break;
          case QgsSQLStatement::ntFunction:
            sipType = sipType_QgsSQLStatement_NodeFunction;
            break;
          case QgsSQLStatement::ntLiteral:
            sipType = sipType_QgsSQLStatement_NodeLiteral;
            break;
          case QgsSQLStatement::ntColumnRef:
            sipType = sipType_QgsSQLStatement_NodeColumnRef;
            break;
          case QgsSQLStatement::ntSelectedColumn:
            sipType = sipType_QgsSQLStatement_NodeSelectedColumn;
            break;
          case QgsSQLStatement::ntSelect:
            sipType = sipType_QgsSQLStatement_NodeSelect;
            break;
          case QgsSQLStatement::ntTableDef:
            sipType = sipType_QgsSQLStatement_NodeTableDef;
            break;
          case QgsSQLStatement::ntJoin:
            sipType = sipType_QgsSQLStatement_NodeJoin;
            break;
          case QgsSQLStatement::ntColumnSorted:
            sipType = sipType_QgsSQLStatement_NodeColumnSorted;
            break;
          case QgsSQLStatement::ntCast:
            sipType = sipType_QgsSQLStatement_NodeCast;
            break;
          default:
            sipType = 0;
            break;
        }
      SIP_END
#endif

      public:
        virtual ~Node() = default;

        /**
         * Abstract virtual that returns the type of this node.
         *
         * \returns The type of this node
         */
        virtual QgsSQLStatement::NodeType nodeType() const = 0;

        /**
         * Abstract virtual dump method
         *
         * \returns A statement which represents this node as string
         */
        virtual QString dump() const = 0;

        /**
         * Generate a clone of this node.
         * Make sure that the clone does not contain any information which is
         * generated in prepare and context related.
         * Ownership is transferred to the caller.
         *
         * \returns a deep copy of this node.
         */
        virtual QgsSQLStatement::Node *clone() const = 0 SIP_FACTORY;

        /**
         * Support the visitor pattern.
         *
         * For any implementation this should look like
         *
         * \code{.cpp}
         *   v.visit( *this );
         * \endcode
         *
         * \code{py}
         *   v.visit(self)
         * \endcode
         *
         * \param v A visitor that visits this node.
         */
        virtual void accept( QgsSQLStatement::Visitor &v ) const = 0;
    };

    /**
     * A list of nodes.
     * \ingroup core
     */
    class CORE_EXPORT NodeList
    {
      public:
        NodeList() = default;
        virtual ~NodeList() { qDeleteAll( mList ); }

        //! Takes ownership of the provided node
        void append( QgsSQLStatement::Node *node SIP_TRANSFER ) { mList.append( node ); }

        //! Returns list
        QList<QgsSQLStatement::Node *> list() { return mList; }

        /**
         * Returns the number of nodes in the list.
         */
        int count() const { return mList.count(); }

        //! Accept visitor
        void accept( QgsSQLStatement::Visitor &v ) const;

        //! Creates a deep copy of this list. Ownership is transferred to the caller
        QgsSQLStatement::NodeList *clone() const SIP_FACTORY;

        //! Dump list
        virtual QString dump() const;

      protected:
        QList<Node *> mList;
    };

    /**
     * \ingroup core
     * \brief Unary logical/arithmetical operator ( NOT, - ).
    */
    class CORE_EXPORT NodeUnaryOperator : public QgsSQLStatement::Node
    {
      public:
        //! Constructor
        NodeUnaryOperator( QgsSQLStatement::UnaryOperator op, QgsSQLStatement::Node *operand SIP_TRANSFER )
          : mOp( op )
          , mOperand( operand )
        {}

        //! Operator
        QgsSQLStatement::UnaryOperator op() const { return mOp; }

        //! Operand
        QgsSQLStatement::Node *operand() const { return mOperand.get(); }

        QgsSQLStatement::NodeType nodeType() const override { return ntUnaryOperator; }
        QString dump() const override;

        void accept( QgsSQLStatement::Visitor &v ) const override { v.visit( *this ); }
        QgsSQLStatement::Node *clone() const override SIP_FACTORY;

      private:
        NodeUnaryOperator( const NodeUnaryOperator &other ) = delete;
        NodeUnaryOperator &operator=( const NodeUnaryOperator &other ) = delete;

#ifdef SIP_RUN
        NodeUnaryOperator( const NodeUnaryOperator &other );
#endif

      protected:
        UnaryOperator mOp;
        std::unique_ptr<Node> mOperand;
    };

    /**
     * \ingroup core
     * \brief Binary logical/arithmetical operator (AND, OR, =, +, ...).
    */
    class CORE_EXPORT NodeBinaryOperator : public QgsSQLStatement::Node
    {
      public:
        //! Constructor
        NodeBinaryOperator( QgsSQLStatement::BinaryOperator op, QgsSQLStatement::Node *opLeft SIP_TRANSFER, QgsSQLStatement::Node *opRight SIP_TRANSFER )
          : mOp( op )
          , mOpLeft( opLeft )
          , mOpRight( opRight )
        {}

        //! Operator
        QgsSQLStatement::BinaryOperator op() const { return mOp; }

        //! Left operand
        QgsSQLStatement::Node *opLeft() const { return mOpLeft.get(); }

        //! Right operand
        QgsSQLStatement::Node *opRight() const { return mOpRight.get(); }

        QgsSQLStatement::NodeType nodeType() const override { return ntBinaryOperator; }
        QString dump() const override;

        void accept( QgsSQLStatement::Visitor &v ) const override { v.visit( *this ); }
        QgsSQLStatement::Node *clone() const override SIP_FACTORY;

        //! Precedence
        int precedence() const;

        //! Is left associative ?
        bool leftAssociative() const;

      private:
        NodeBinaryOperator( const NodeBinaryOperator &other ) = delete;
        NodeBinaryOperator &operator=( const NodeBinaryOperator &other ) = delete;

#ifdef SIP_RUN
        NodeBinaryOperator( const NodeBinaryOperator &other );
#endif

      protected:
        BinaryOperator mOp;
        std::unique_ptr<Node> mOpLeft;
        std::unique_ptr<Node> mOpRight;
    };

    /**
     * \ingroup core
     * \brief An 'x IN (y, z)' operator.
    */
    class CORE_EXPORT NodeInOperator : public QgsSQLStatement::Node
    {
      public:
        //! Constructor
        NodeInOperator( QgsSQLStatement::Node *node SIP_TRANSFER, QgsSQLStatement::NodeList *list SIP_TRANSFER, bool notin = false )
          : mNode( node )
          , mList( list )
          , mNotIn( notin )
        {}

        //! Variable at the left of IN
        QgsSQLStatement::Node *node() const { return mNode.get(); }

        //! Whether this is a NOT IN operator
        bool isNotIn() const { return mNotIn; }

        //! Values list
        QgsSQLStatement::NodeList *list() const { return mList.get(); }

        QgsSQLStatement::NodeType nodeType() const override { return ntInOperator; }
        QString dump() const override;

        void accept( QgsSQLStatement::Visitor &v ) const override { v.visit( *this ); }
        QgsSQLStatement::Node *clone() const override SIP_FACTORY;

      private:
        NodeInOperator( const NodeInOperator &other ) = delete;
        NodeInOperator &operator=( const NodeInOperator &other ) = delete;

#ifdef SIP_RUN
        NodeInOperator( const NodeInOperator &other );
#endif

      protected:
        std::unique_ptr<Node> mNode;
        std::unique_ptr<NodeList> mList;
        bool mNotIn;
    };

    /**
     * \ingroup core
     * \brief An 'X BETWEEN y and z' operator.
    */
    class CORE_EXPORT NodeBetweenOperator : public QgsSQLStatement::Node
    {
      public:
        //! Constructor
        NodeBetweenOperator( QgsSQLStatement::Node *node SIP_TRANSFER, QgsSQLStatement::Node *minVal SIP_TRANSFER, QgsSQLStatement::Node *maxVal SIP_TRANSFER, bool notBetween = false )
          : mNode( node )
          , mMinVal( minVal )
          , mMaxVal( maxVal )
          , mNotBetween( notBetween )
        {}

        //! Variable at the left of BETWEEN
        QgsSQLStatement::Node *node() const { return mNode.get(); }

        //! Whether this is a NOT BETWEEN operator
        bool isNotBetween() const { return mNotBetween; }

        //! Minimum bound
        QgsSQLStatement::Node *minVal() const { return mMinVal.get(); }

        //! Maximum bound
        QgsSQLStatement::Node *maxVal() const { return mMaxVal.get(); }

        QgsSQLStatement::NodeType nodeType() const override { return ntBetweenOperator; }
        QString dump() const override;

        void accept( QgsSQLStatement::Visitor &v ) const override { v.visit( *this ); }
        QgsSQLStatement::Node *clone() const override SIP_FACTORY;

      private:
        NodeBetweenOperator( const NodeBetweenOperator &other ) = delete;
        NodeBetweenOperator &operator=( const NodeBetweenOperator &other ) = delete;

#ifdef SIP_RUN
        NodeBetweenOperator( const NodeBetweenOperator &other );
#endif

      protected:
        std::unique_ptr<Node> mNode;
        std::unique_ptr<Node> mMinVal;
        std::unique_ptr<Node> mMaxVal;
        bool mNotBetween;
    };

    /**
     * \ingroup core
     * \brief Function with a name and arguments node.
    */
    class CORE_EXPORT NodeFunction : public QgsSQLStatement::Node
    {
      public:
        //! Constructor
        NodeFunction( const QString &name, QgsSQLStatement::NodeList *args SIP_TRANSFER )
          : mName( name )
          , mArgs( args )
        {}

        //! Returns function name
        QString name() const { return mName; }

        //! Returns arguments
        QgsSQLStatement::NodeList *args() const { return mArgs.get(); }

        QgsSQLStatement::NodeType nodeType() const override { return ntFunction; }
        QString dump() const override;

        void accept( QgsSQLStatement::Visitor &v ) const override { v.visit( *this ); }
        QgsSQLStatement::Node *clone() const override SIP_FACTORY;

      private:
        NodeFunction( const NodeFunction &other ) = delete;
        NodeFunction &operator=( const NodeFunction &other ) = delete;

#ifdef SIP_RUN
        NodeFunction( const NodeFunction &other );
#endif

      protected:
        QString mName;
        std::unique_ptr<NodeList> mArgs;
    };

    /**
     * \ingroup core
     * \brief Literal value (integer, integer64, double, string).
    */
    class CORE_EXPORT NodeLiteral : public QgsSQLStatement::Node
    {
      public:
        //! Constructor
        NodeLiteral( const QVariant &value )
          : mValue( value )
        {}

        //! The value of the literal.
        inline QVariant value() const { return mValue; }

        QgsSQLStatement::NodeType nodeType() const override { return ntLiteral; }
        QString dump() const override;

        void accept( QgsSQLStatement::Visitor &v ) const override { v.visit( *this ); }
        QgsSQLStatement::Node *clone() const override SIP_FACTORY;

      protected:
        QVariant mValue;
    };

    /**
     * \ingroup core
     * \brief Reference to a column.
    */
    class CORE_EXPORT NodeColumnRef : public QgsSQLStatement::Node
    {
      public:
        //! Constructor with column name only
        NodeColumnRef( const QString &name, bool star )
          : mName( name )
          , mDistinct( false )
          , mStar( star )
        {}
        //! Constructor with table and column name
        NodeColumnRef( const QString &tableName, const QString &name, bool star )
          : mTableName( tableName )
          , mName( name )
          , mDistinct( false )
          , mStar( star )
        {}

        //! Sets whether this is prefixed by DISTINCT
        void setDistinct( bool distinct = true ) { mDistinct = distinct; }

        //! The name of the table. May be empty.
        QString tableName() const { return mTableName; }

        //! The name of the column.
        QString name() const { return mName; }

        //! Whether this is the * column
        bool star() const { return mStar; }

        //! Whether this is prefixed by DISTINCT
        bool distinct() const { return mDistinct; }

        QgsSQLStatement::NodeType nodeType() const override { return ntColumnRef; }
        QString dump() const override;

        void accept( QgsSQLStatement::Visitor &v ) const override { v.visit( *this ); }
        QgsSQLStatement::Node *clone() const override SIP_FACTORY;
        //! Clone with same type return
        QgsSQLStatement::NodeColumnRef *cloneThis() const SIP_FACTORY;

      protected:
        QString mTableName;
        QString mName;
        bool mDistinct;
        bool mStar;
    };

    /**
     * \ingroup core
     * \brief Selected column.
    */
    class CORE_EXPORT NodeSelectedColumn : public QgsSQLStatement::Node
    {
      public:
        //! Constructor
        NodeSelectedColumn( QgsSQLStatement::Node *node SIP_TRANSFER )
          : mColumnNode( node )
        {}

        //! Sets alias name
        void setAlias( const QString &alias ) { mAlias = alias; }

        //! Column that is referred to
        QgsSQLStatement::Node *column() const { return mColumnNode.get(); }

        //! Alias name
        QString alias() const { return mAlias; }

        QgsSQLStatement::NodeType nodeType() const override { return ntSelectedColumn; }
        QString dump() const override;

        void accept( QgsSQLStatement::Visitor &v ) const override { v.visit( *this ); }
        QgsSQLStatement::Node *clone() const override SIP_FACTORY;
        //! Clone with same type return
        QgsSQLStatement::NodeSelectedColumn *cloneThis() const SIP_FACTORY;

      private:
        NodeSelectedColumn( const NodeSelectedColumn &other ) = delete;
        NodeSelectedColumn &operator=( const NodeSelectedColumn &other ) = delete;

#ifdef SIP_RUN
        NodeSelectedColumn( const NodeSelectedColumn &other );
#endif

      protected:
        std::unique_ptr<Node> mColumnNode;
        QString mAlias;
    };

    /**
     * \ingroup core
     * \brief CAST operator.
    */
    class CORE_EXPORT NodeCast : public QgsSQLStatement::Node
    {
      public:
        //! Constructor
        NodeCast( QgsSQLStatement::Node *node SIP_TRANSFER, const QString &type )
          : mNode( node )
          , mType( type )
        {}

        //! Node that is referred to
        QgsSQLStatement::Node *node() const { return mNode.get(); }

        //! Type
        QString type() const { return mType; }

        QgsSQLStatement::NodeType nodeType() const override { return ntCast; }
        QString dump() const override;

        void accept( QgsSQLStatement::Visitor &v ) const override { v.visit( *this ); }
        QgsSQLStatement::Node *clone() const override SIP_FACTORY;

      private:
        NodeCast( const NodeCast &other ) = delete;
        NodeCast &operator=( const NodeCast &other ) = delete;

#ifdef SIP_RUN
        NodeCast( const NodeCast &other );
#endif

      protected:
        std::unique_ptr<Node> mNode;
        QString mType;
    };

    /**
     * \ingroup core
     * \brief Table definition.
    */
    class CORE_EXPORT NodeTableDef : public QgsSQLStatement::Node
    {
      public:
        //! Constructor with table name
        NodeTableDef( const QString &name )
          : mName( name )
        {}
        //! Constructor with table name and alias
        NodeTableDef( const QString &name, const QString &alias )
          : mName( name )
          , mAlias( alias )
        {}

        /**
         * Constructor with schema, table name and alias
         * \since QGIS 3.28
         */
        NodeTableDef( const QString &schema, const QString &name, const QString &alias )
          : mName( name )
          , mSchema( schema )
          , mAlias( alias )
        {}

        //! Table name
        QString name() const { return mName; }

        /**
         * Returns the schema name.
         *
         * \since QGIS 3.28
         */
        QString schema() const { return mSchema; }

        //! Table alias
        QString alias() const { return mAlias; }

        QgsSQLStatement::NodeType nodeType() const override { return ntTableDef; }
        QString dump() const override;

        void accept( QgsSQLStatement::Visitor &v ) const override { v.visit( *this ); }
        QgsSQLStatement::Node *clone() const override SIP_FACTORY;
        //! Clone with same type return
        QgsSQLStatement::NodeTableDef *cloneThis() const SIP_FACTORY;

      protected:
        QString mName;
        QString mSchema;
        QString mAlias;
    };

    /**
     * \ingroup core
     * \brief Join definition.
    */
    class CORE_EXPORT NodeJoin : public QgsSQLStatement::Node
    {
      public:
        //! Constructor with table definition, ON expression
        NodeJoin( QgsSQLStatement::NodeTableDef *tabledef SIP_TRANSFER, QgsSQLStatement::Node *onExpr SIP_TRANSFER, QgsSQLStatement::JoinType type )
          : mTableDef( tabledef )
          , mOnExpr( onExpr )
          , mType( type )
        {}
        //! Constructor with table definition and USING columns
        NodeJoin( QgsSQLStatement::NodeTableDef *tabledef SIP_TRANSFER, const QList<QString> &usingColumns, QgsSQLStatement::JoinType type )
          : mTableDef( tabledef )
          , mUsingColumns( usingColumns )
          , mType( type )
        {}

        //! Table definition
        QgsSQLStatement::NodeTableDef *tableDef() const { return mTableDef.get(); }

        //! On expression. Will be NULLPTR if usingColumns() is not empty
        QgsSQLStatement::Node *onExpr() const { return mOnExpr.get(); }

        //! Columns referenced by USING
        QList<QString> usingColumns() const { return mUsingColumns; }

        //! Join type
        QgsSQLStatement::JoinType type() const { return mType; }

        QgsSQLStatement::NodeType nodeType() const override { return ntJoin; }
        QString dump() const override;

        void accept( QgsSQLStatement::Visitor &v ) const override { v.visit( *this ); }
        QgsSQLStatement::Node *clone() const override SIP_FACTORY;
        //! Clone with same type return
        QgsSQLStatement::NodeJoin *cloneThis() const SIP_FACTORY;

      private:
        NodeJoin( const NodeJoin &other ) = delete;
        NodeJoin &operator=( const NodeJoin &other ) = delete;

#ifdef SIP_RUN
        NodeJoin( const NodeJoin &other );
#endif

      protected:
        std::unique_ptr<NodeTableDef> mTableDef;
        std::unique_ptr<Node> mOnExpr;
        QList<QString> mUsingColumns;
        JoinType mType;
    };

    /**
     * \ingroup core
     * \brief Column in a ORDER BY.
    */
    class CORE_EXPORT NodeColumnSorted : public QgsSQLStatement::Node
    {
      public:
        //! Constructor
        NodeColumnSorted( QgsSQLStatement::NodeColumnRef *column SIP_TRANSFER, bool asc )
          : mColumn( column )
          , mAsc( asc )
        {}

        //! The name of the column.
        QgsSQLStatement::NodeColumnRef *column() const { return mColumn.get(); }

        //! Whether the column is sorted in ascending order
        bool ascending() const { return mAsc; }

        QgsSQLStatement::NodeType nodeType() const override { return ntColumnSorted; }
        QString dump() const override;

        void accept( QgsSQLStatement::Visitor &v ) const override { v.visit( *this ); }
        QgsSQLStatement::Node *clone() const override SIP_FACTORY;
        //! Clone with same type return
        QgsSQLStatement::NodeColumnSorted *cloneThis() const SIP_FACTORY;

      private:
        NodeColumnSorted( const NodeColumnSorted &other ) = delete;
        NodeColumnSorted &operator=( const NodeColumnSorted &other ) = delete;

#ifdef SIP_RUN
        NodeColumnSorted( const NodeColumnSorted &other );
#endif

      protected:
        std::unique_ptr<NodeColumnRef> mColumn;
        bool mAsc;
    };

    /**
     * \ingroup core
     * \brief SELECT node.
    */
    class CORE_EXPORT NodeSelect : public QgsSQLStatement::Node
    {
      public:
        //! Constructor
        NodeSelect( const QList<QgsSQLStatement::NodeTableDef *> &tableList SIP_TRANSFER, const QList<QgsSQLStatement::NodeSelectedColumn *> &columns SIP_TRANSFER, bool distinct )
          : mTableList( tableList )
          , mColumns( columns )
          , mDistinct( distinct )
        {}
        ~NodeSelect() override;

        //! Sets joins
        void setJoins( const QList<QgsSQLStatement::NodeJoin *> &joins SIP_TRANSFER )
        {
          qDeleteAll( mJoins );
          mJoins = joins;
        }
        //! Append a join
        void appendJoin( QgsSQLStatement::NodeJoin *join SIP_TRANSFER ) { mJoins.append( join ); }
        //! Sets where clause
        void setWhere( QgsSQLStatement::Node *where SIP_TRANSFER ) { mWhere.reset( where ); }
        //! Sets order by columns
        void setOrderBy( const QList<QgsSQLStatement::NodeColumnSorted *> &orderBy SIP_TRANSFER )
        {
          qDeleteAll( mOrderBy );
          mOrderBy = orderBy;
        }

        //! Returns the list of tables
        QList<QgsSQLStatement::NodeTableDef *> tables() const { return mTableList; }
        //! Returns the list of columns
        QList<QgsSQLStatement::NodeSelectedColumn *> columns() const { return mColumns; }
        //! Returns if the SELECT is DISTINCT
        bool distinct() const { return mDistinct; }
        //! Returns the list of joins
        QList<QgsSQLStatement::NodeJoin *> joins() const { return mJoins; }
        //! Returns the where clause
        QgsSQLStatement::Node *where() const { return mWhere.get(); }
        //! Returns the list of order by columns
        QList<QgsSQLStatement::NodeColumnSorted *> orderBy() const { return mOrderBy; }

        QgsSQLStatement::NodeType nodeType() const override { return ntSelect; }
        QString dump() const override;

        void accept( QgsSQLStatement::Visitor &v ) const override { v.visit( *this ); }
        QgsSQLStatement::Node *clone() const override SIP_FACTORY;

      private:
        NodeSelect( const NodeSelect &other ) = delete;
        NodeSelect &operator=( const NodeSelect &other ) = delete;

#ifdef SIP_RUN
        NodeSelect( const NodeSelect &other );
#endif

      protected:
        QList<NodeTableDef *> mTableList;
        QList<NodeSelectedColumn *> mColumns;
        bool mDistinct;
        QList<NodeJoin *> mJoins;
        std::unique_ptr<Node> mWhere;
        QList<NodeColumnSorted *> mOrderBy;
    };

    //////

    /**
     * \ingroup core
     * \brief Support for visitor pattern - algorithms dealing with the statement
     * may be implemented without modifying the Node classes.
    */
    class CORE_EXPORT Visitor
    {
      public:
        virtual ~Visitor() = default;
        //! Visit NodeUnaryOperator
        virtual void visit( const QgsSQLStatement::NodeUnaryOperator &n ) = 0;
        //! Visit NodeBinaryOperator
        virtual void visit( const QgsSQLStatement::NodeBinaryOperator &n ) = 0;
        //! Visit NodeInOperator
        virtual void visit( const QgsSQLStatement::NodeInOperator &n ) = 0;
        //! Visit NodeBetweenOperator
        virtual void visit( const QgsSQLStatement::NodeBetweenOperator &n ) = 0;
        //! Visit NodeFunction
        virtual void visit( const QgsSQLStatement::NodeFunction &n ) = 0;
        //! Visit NodeLiteral
        virtual void visit( const QgsSQLStatement::NodeLiteral &n ) = 0;
        //! Visit NodeColumnRef
        virtual void visit( const QgsSQLStatement::NodeColumnRef &n ) = 0;
        //! Visit NodeSelectedColumn
        virtual void visit( const QgsSQLStatement::NodeSelectedColumn &n ) = 0;
        //! Visit NodeTableDef
        virtual void visit( const QgsSQLStatement::NodeTableDef &n ) = 0;
        //! Visit NodeSelect
        virtual void visit( const QgsSQLStatement::NodeSelect &n ) = 0;
        //! Visit NodeJoin
        virtual void visit( const QgsSQLStatement::NodeJoin &n ) = 0;
        //! Visit NodeColumnSorted
        virtual void visit( const QgsSQLStatement::NodeColumnSorted &n ) = 0;
        //! Visit NodeCast
        virtual void visit( const QgsSQLStatement::NodeCast &n ) = 0;
    };

    /**
     * \ingroup core
     * \brief A visitor that recursively explores all children.
    */
    class CORE_EXPORT RecursiveVisitor : public QgsSQLStatement::Visitor
    {
      public:
        RecursiveVisitor() = default;

        void visit( const QgsSQLStatement::NodeUnaryOperator &n ) override { n.operand()->accept( *this ); }
        void visit( const QgsSQLStatement::NodeBinaryOperator &n ) override
        {
          n.opLeft()->accept( *this );
          n.opRight()->accept( *this );
        }
        void visit( const QgsSQLStatement::NodeInOperator &n ) override
        {
          n.node()->accept( *this );
          n.list()->accept( *this );
        }
        void visit( const QgsSQLStatement::NodeBetweenOperator &n ) override
        {
          n.node()->accept( *this );
          n.minVal()->accept( *this );
          n.maxVal()->accept( *this );
        }
        void visit( const QgsSQLStatement::NodeFunction &n ) override { n.args()->accept( *this ); }
        void visit( const QgsSQLStatement::NodeLiteral & ) override {}
        void visit( const QgsSQLStatement::NodeColumnRef & ) override {}
        void visit( const QgsSQLStatement::NodeSelectedColumn &n ) override { n.column()->accept( *this ); }
        void visit( const QgsSQLStatement::NodeTableDef & ) override {}
        void visit( const QgsSQLStatement::NodeSelect &n ) override;
        void visit( const QgsSQLStatement::NodeJoin &n ) override;
        void visit( const QgsSQLStatement::NodeColumnSorted &n ) override { n.column()->accept( *this ); }
        void visit( const QgsSQLStatement::NodeCast &n ) override { n.node()->accept( *this ); }
    };

    //! Entry function for the visitor pattern
    void acceptVisitor( QgsSQLStatement::Visitor &v ) const;

  protected:
    std::unique_ptr<QgsSQLStatement::Node> mRootNode;
    bool mAllowFragments = false;
    QString mStatement;
    QString mParserErrorString;

    /**
     * Constructor for QgsSQLStatement, with the specified \a statement.
     *
     * If \a allowFragments is TRUE then the parser will allow SQL fragments,
     * such as a expression or filter where clause alone.
     *
     * \since QGIS 3.16
     */
    QgsSQLStatement( const QString &statement, bool allowFragments );
};

Q_DECLARE_METATYPE( QgsSQLStatement::Node * )

/**
 * \ingroup core
 * \brief Parses fragments of SQL statements, such as an expression or where clause.
 * \since QGIS 3.16
*/
class CORE_EXPORT QgsSQLStatementFragment : public QgsSQLStatement
{
  public:
    /**
     * Constructor for QgsSQLStatementFragment of the specified \a fragment.
     */
    QgsSQLStatementFragment( const QString &fragment );
};


#endif // QGSSQLSTATEMENT_H
