syntax = "proto3";

package substrait;

import "substrait/type.proto";

option java_multiple_files = true;
option java_package = "io.substrait.proto";
option csharp_namespace = "Substrait.Protobuf";

message Expression {
  oneof rex_type {
    Literal literal = 1;
    FieldReference selection = 2;
    ScalarFunction scalar_function = 3;
    WindowFunction window_function = 5;
    IfThen if_then = 6;
    SwitchExpression switch_expression = 7;
    SingularOrList singular_or_list = 8;
    MultiOrList multi_or_list = 9;
    Enum enum = 10;
    Cast cast = 11;
  }

  message Enum {
    oneof enum_kind {
      string specified = 1;
      Empty unspecified = 2;
    }

    message Empty {}
  }

  message Literal {
    oneof literal_type {
      bool boolean = 1;
      int32 i8 = 2;
      int32 i16 = 3;
      int32 i32 = 5;
      int64 i64 = 7;
      float fp32 = 10;
      double fp64 = 11;
      string string = 12;
      bytes binary = 13;
      // Timestamp in units of microseconds since the UNIX epoch.
      int64 timestamp = 14;
      // Date in units of days since the UNIX epoch.
      int32 date = 16;
      // Time in units of microseconds past midnight
      int64 time = 17;
      IntervalYearToMonth interval_year_to_month = 19;
      IntervalDayToSecond interval_day_to_second = 20;
      string fixed_char = 21;
      VarChar var_char = 22;
      bytes fixed_binary = 23;
      Decimal decimal = 24;
      Struct struct = 25;
      Map map = 26;
      // Timestamp in units of microseconds since the UNIX epoch.
      int64 timestamp_tz = 27;
      bytes uuid = 28;
      Type null = 29; // a typed null literal
      List list = 30;
      Type.List empty_list = 31;
      Type.Map empty_map = 32;
    }

    // whether the literal type should be treated as a nullable type. Applies to
    // all members of union other than the Typed null (which should directly
    // declare nullability).
    bool nullable = 50;

    message VarChar {
      string value = 1;
      uint32 length = 2;
    }

    message Decimal {
      // little-endian twos-complement integer representation of complete value
      // (ignoring precision) Always 16 bytes in length
      bytes value = 1;
      // The maximum number of digits allowed in the value.
      // the maximum precision is 38.
      int32 precision = 2;
      // declared scale of decimal literal
      int32 scale = 3;
    }

    message Map {
      message KeyValue {
        Literal key = 1;
        Literal value = 2;
      }

      repeated KeyValue key_values = 1;
    }

    message IntervalYearToMonth {
      int32 years = 1;
      int32 months = 2;
    }

    message IntervalDayToSecond {
      int32 days = 1;
      int32 seconds = 2;
    }

    message Struct {
      // A possibly heterogeneously typed list of literals
      repeated Literal fields = 1;
    }

    message List {
      // A homogeneously typed list of literals
      repeated Literal values = 1;
    }
  }

  message ScalarFunction {
    // points to a function_anchor defined in this plan
    uint32 function_reference = 1;
    repeated Expression args = 2;
    Type output_type = 3;
  }

  message WindowFunction {
    // points to a function_anchor defined in this plan
    uint32 function_reference = 1;
    repeated Expression partitions = 2;
    repeated SortField sorts = 3;
    Bound upper_bound = 4;
    Bound lower_bound = 5;
    AggregationPhase phase = 6;
    Type output_type = 7;
    repeated Expression args = 8;

    message Bound {

      message Preceding { int64 offset = 1; }

      message Following { int64 offset = 1; }

      message CurrentRow {}

      message Unbounded {}

      oneof kind {
        Preceding preceding = 1;
        Following following = 2;
        CurrentRow current_row = 3;
        Unbounded unbounded = 4;
      }
    }
  }

  message IfThen {

    repeated IfClause ifs = 1;
    Expression else = 2;

    message IfClause {
      Expression if = 1;
      Expression then = 2;
    }
  }

  message Cast {
    Type type = 1;
    Expression input = 2;
  }

  message SwitchExpression {
    repeated IfValue ifs = 1;
    Expression else = 2;

    message IfValue {
      Literal if = 1;
      Expression then = 2;
    }
  }

  message SingularOrList {
    Expression value = 1;
    repeated Expression options = 2;
  }

  message MultiOrList {
    repeated Expression value = 1;
    repeated Record options = 2;

    message Record { repeated Expression fields = 1; }
  }

  message EmbeddedFunction {
    repeated Expression arguments = 1;
    Type output_type = 2;
    oneof kind {
      PythonPickleFunction python_pickle_function = 3;
      WebAssemblyFunction web_assembly_function = 4;
    }

    message PythonPickleFunction {
      bytes function = 1;
      repeated string prerequisite = 2;
    }

    message WebAssemblyFunction {
      bytes script = 1;
      repeated string prerequisite = 2;
    }
  }

  // A way to reference the inner property of a complex record. Can reference
  // either a map key by literal, a struct field by the ordinal position of
  // the desired field or a particular element in an array. Supports
  // expressions that would roughly translate to something similar to:
  // a.b[2].c['my_map_key'].x where a,b,c and x are struct field references
  // (ordinalized in the internal representation here), [2] is a list offset
  // and ['my_map_key'] is a reference into a map field.
  message ReferenceSegment {

    oneof reference_type {
      MapKey map_key = 1;
      StructField struct_field = 2;
      ListElement list_element = 3;
    }

    message MapKey {
      // literal based reference to specific possible value in map.
      Literal map_key = 1;

      // Optional child segment
      ReferenceSegment child = 2;
    }

    message StructField {
      // zero-indexed ordinal position of field in struct
      int32 field = 1;

      // Optional child segment
      ReferenceSegment child = 2;
    }

    message ListElement {
      // zero-indexed ordinal position of element in list
      int32 offset = 1;

      // Optional child segment
      ReferenceSegment child = 2;
    }

  }

  // A reference that takes an existing subtype and selectively removes fields from
  // it. For example, one might initially have an inner struct with 100 fields but a
  // a particular operation only needs to interact with only 2 of those 100 fields.
  // In this situation, one would use a mask expression to eliminate the 98 fields that
  // are not relevant to the rest of the operation pipeline.
  //
  // Note that this does not fundamentally alter the structure of data beyond the
  // elimination of unecessary elements.
  message MaskExpression {

    StructSelect select = 1;
    bool maintain_singular_struct = 2;

    message Select {
      oneof type {
        StructSelect struct = 1;
        ListSelect list = 2;
        MapSelect map = 3;
      }
    }

    message StructSelect { repeated StructItem struct_items = 1; }

    message StructItem {
      int32 field = 1;
      Select child = 2;
    }

    message ListSelect {

      repeated ListSelectItem selection = 1;
      Select child = 2;

      message ListSelectItem {
        oneof type {
          ListElement item = 1;
          ListSlice slice = 2;
        }

        message ListElement { int32 field = 1; }

        message ListSlice {
          int32 start = 1;
          int32 end = 2;
        }
      }
    }

    message MapSelect {
      oneof select {
        MapKey key = 1;
        MapKeyExpression expression = 2;
      }

      Select child = 3;

      message MapKey { string map_key = 1; }

      message MapKeyExpression { string map_key_expression = 1; }
    }
  }

  // A reference to an inner part of a complex object. Can reference reference a single
  // element or a masked version of elements
  message FieldReference {

    // Whether this is composed of a single element reference or a masked element subtree
    oneof reference_type {
      ReferenceSegment direct_reference = 1;
      MaskExpression masked_reference = 2;
    }

    // Whether this reference has an origin of a root struct or is based on the ouput
    // of an expression. When this is a RootReference and direct_reference above is used,
    // the direct_reference must be of a type StructField.
    oneof root_type {
      Expression expression = 3;
      RootReference root_reference = 4;
    }

    // Singleton that expresses this FieldReference is rooted off the root incoming record type
    message RootReference {}

  }
}

message SortField {
  Expression expr = 1;

  oneof sort_kind {
    SortDirection direction = 2;
    uint32 comparison_function_reference = 3;
  }
  enum SortDirection {
    SORT_DIRECTION_UNSPECIFIED = 0;
    SORT_DIRECTION_ASC_NULLS_FIRST = 1;
    SORT_DIRECTION_ASC_NULLS_LAST = 2;
    SORT_DIRECTION_DESC_NULLS_FIRST = 3;
    SORT_DIRECTION_DESC_NULLS_LAST = 4;
    SORT_DIRECTION_CLUSTERED = 5;
  }
}

enum AggregationPhase {
  AGGREGATION_PHASE_UNSPECIFIED = 0;
  AGGREGATION_PHASE_INITIAL_TO_INTERMEDIATE = 1;
  AGGREGATION_PHASE_INTERMEDIATE_TO_INTERMEDIATE = 2;
  AGGREGATION_PHASE_INITIAL_TO_RESULT = 3;
  AGGREGATION_PHASE_INTERMEDIATE_TO_RESULT = 4;
}

message AggregateFunction {
  // points to a function_anchor defined in this plan
  uint32 function_reference = 1;
  repeated Expression args = 2;
  repeated SortField sorts = 3;
  AggregationPhase phase = 4;
  Type output_type = 5;
}
