66 */ 
77package  org .elasticsearch .xpack .esql .core .expression ;
88
9+ import  org .elasticsearch .TransportVersions ;
910import  org .elasticsearch .common .Strings ;
1011import  org .elasticsearch .common .io .stream .NamedWriteableRegistry ;
1112import  org .elasticsearch .common .io .stream .StreamInput ;
1213import  org .elasticsearch .common .io .stream .StreamOutput ;
14+ import  org .elasticsearch .core .Nullable ;
1315import  org .elasticsearch .xpack .esql .core .tree .NodeInfo ;
1416import  org .elasticsearch .xpack .esql .core .tree .Source ;
1517import  org .elasticsearch .xpack .esql .core .type .DataType ;
1618import  org .elasticsearch .xpack .esql .core .type .EsField ;
1719import  org .elasticsearch .xpack .esql .core .util .PlanStreamInput ;
1820import  org .elasticsearch .xpack .esql .core .util .PlanStreamOutput ;
19- import  org .elasticsearch .xpack .esql .core .util .StringUtils ;
2021
2122import  java .io .IOException ;
2223import  java .util .Objects ;
2324
25+ import  static  org .elasticsearch .xpack .esql .core .util .PlanStreamInput .readCachedStringWithVersionCheck ;
26+ import  static  org .elasticsearch .xpack .esql .core .util .PlanStreamOutput .writeCachedStringWithVersionCheck ;
27+ 
2428/** 
2529 * Attribute for an ES field. 
2630 * To differentiate between the different type of fields this class offers: 
@@ -37,32 +41,31 @@ public class FieldAttribute extends TypedAttribute {
3741        FieldAttribute ::readFrom 
3842    );
3943
40-     private  final  FieldAttribute  parent ;
41-     private  final  String  path ;
44+     private  final  String  parentName ;
4245    private  final  EsField  field ;
4346
4447    public  FieldAttribute (Source  source , String  name , EsField  field ) {
4548        this (source , null , name , field );
4649    }
4750
48-     public  FieldAttribute (Source  source , FieldAttribute   parent , String  name , EsField  field ) {
49-         this (source , parent , name , field , Nullability .TRUE , null , false );
51+     public  FieldAttribute (Source  source , @ Nullable   String   parentName , String  name , EsField  field ) {
52+         this (source , parentName , name , field , Nullability .TRUE , null , false );
5053    }
5154
52-     public  FieldAttribute (Source  source , FieldAttribute   parent , String  name , EsField  field , boolean  synthetic ) {
53-         this (source , parent , name , field , Nullability .TRUE , null , synthetic );
55+     public  FieldAttribute (Source  source , @ Nullable   String   parentName , String  name , EsField  field , boolean  synthetic ) {
56+         this (source , parentName , name , field , Nullability .TRUE , null , synthetic );
5457    }
5558
5659    public  FieldAttribute (
5760        Source  source ,
58-         FieldAttribute   parent ,
61+         @ Nullable   String   parentName ,
5962        String  name ,
6063        EsField  field ,
6164        Nullability  nullability ,
62-         NameId  id ,
65+         @ Nullable   NameId  id ,
6366        boolean  synthetic 
6467    ) {
65-         this (source , parent , name , field .getDataType (), field , nullability , id , synthetic );
68+         this (source , parentName , name , field .getDataType (), field , nullability , id , synthetic );
6669    }
6770
6871    /** 
@@ -71,17 +74,16 @@ public FieldAttribute(
7174     */ 
7275    FieldAttribute (
7376        Source  source ,
74-         FieldAttribute   parent ,
77+         @ Nullable   String   parentName ,
7578        String  name ,
7679        DataType  type ,
7780        EsField  field ,
7881        Nullability  nullability ,
79-         NameId  id ,
82+         @ Nullable   NameId  id ,
8083        boolean  synthetic 
8184    ) {
8285        super (source , name , type , nullability , id , synthetic );
83-         this .path  = parent  != null  ? parent .name () : StringUtils .EMPTY ;
84-         this .parent  = parent ;
86+         this .parentName  = parentName ;
8587        this .field  = field ;
8688    }
8789
@@ -91,16 +93,16 @@ public FieldAttribute(
9193     */ 
9294    private  FieldAttribute (
9395        Source  source ,
94-         FieldAttribute   parent ,
96+         @ Nullable   String   parentName ,
9597        String  name ,
9698        DataType  type ,
9799        EsField  field ,
98-         String  qualifier ,
100+         @ Nullable   String  qualifier ,
99101        Nullability  nullability ,
100-         NameId  id ,
102+         @ Nullable   NameId  id ,
101103        boolean  synthetic 
102104    ) {
103-         this (source , parent , name , type , field , nullability , id , synthetic );
105+         this (source , parentName , name , type , field , nullability , id , synthetic );
104106    }
105107
106108    private  FieldAttribute (StreamInput  in ) throws  IOException  {
@@ -114,8 +116,8 @@ private FieldAttribute(StreamInput in) throws IOException {
114116         */ 
115117        this (
116118            Source .readFrom ((StreamInput  & PlanStreamInput ) in ),
117-             in . readOptionalWriteable ( FieldAttribute :: readFrom ),
118-             (( PlanStreamInput )  in ). readCachedString ( ),
119+             readParentName ( in ),
120+             readCachedStringWithVersionCheck ( in ),
119121            DataType .readFrom (in ),
120122            EsField .readFrom (in ),
121123            in .readOptionalString (),
@@ -129,8 +131,8 @@ private FieldAttribute(StreamInput in) throws IOException {
129131    public  void  writeTo (StreamOutput  out ) throws  IOException  {
130132        if  (((PlanStreamOutput ) out ).writeAttributeCacheHeader (this )) {
131133            Source .EMPTY .writeTo (out );
132-             out . writeOptionalWriteable ( parent );
133-             (( PlanStreamOutput )  out ). writeCachedString ( name ());
134+             writeParentName ( out );
135+             writeCachedStringWithVersionCheck ( out ,  name ());
134136            dataType ().writeTo (out );
135137            field .writeTo (out );
136138            // We used to write the qualifier here. We can still do if needed in the future. 
@@ -145,22 +147,49 @@ public static FieldAttribute readFrom(StreamInput in) throws IOException {
145147        return  ((PlanStreamInput ) in ).readAttributeWithCache (FieldAttribute ::new );
146148    }
147149
150+     private  void  writeParentName (StreamOutput  out ) throws  IOException  {
151+         if  (out .getTransportVersion ().onOrAfter (TransportVersions .ESQL_FIELD_ATTRIBUTE_PARENT_SIMPLIFIED )) {
152+             ((PlanStreamOutput ) out ).writeOptionalCachedString (parentName );
153+         } else  {
154+             // Previous versions only used the parent field attribute to retrieve the parent's name, so we can use just any 
155+             // fake FieldAttribute here as long as the name is correct. 
156+             FieldAttribute  fakeParent  = parentName () == null  ? null  : new  FieldAttribute (Source .EMPTY , parentName (), field ());
157+             out .writeOptionalWriteable (fakeParent );
158+         }
159+     }
160+ 
161+     private  static  String  readParentName (StreamInput  in ) throws  IOException  {
162+         if  (in .getTransportVersion ().onOrAfter (TransportVersions .ESQL_FIELD_ATTRIBUTE_PARENT_SIMPLIFIED )) {
163+             return  ((PlanStreamInput ) in ).readOptionalCachedString ();
164+         }
165+ 
166+         FieldAttribute  parent  = in .readOptionalWriteable (FieldAttribute ::readFrom );
167+         return  parent  == null  ? null  : parent .name ();
168+     }
169+ 
148170    @ Override 
149171    public  String  getWriteableName () {
150172        return  ENTRY .name ;
151173    }
152174
153175    @ Override 
154176    protected  NodeInfo <FieldAttribute > info () {
155-         return  NodeInfo .create (this , FieldAttribute ::new , parent , name (), dataType (), field , (String ) null , nullable (), id (), synthetic ());
156-     }
157- 
158-     public  FieldAttribute  parent () {
159-         return  parent ;
177+         return  NodeInfo .create (
178+             this ,
179+             FieldAttribute ::new ,
180+             parentName ,
181+             name (),
182+             dataType (),
183+             field ,
184+             (String ) null ,
185+             nullable (),
186+             id (),
187+             synthetic ()
188+         );
160189    }
161190
162-     public  String  path () {
163-         return  path ;
191+     public  String  parentName () {
192+         return  parentName ;
164193    }
165194
166195    /** 
@@ -174,7 +203,7 @@ public String fieldName() {
174203        if  ((synthetic () || name ().startsWith (SYNTHETIC_ATTRIBUTE_NAME_PREFIX )) == false ) {
175204            return  name ();
176205        }
177-         return  Strings .hasText (path ) ? path  + "."  + field .getName () : field .getName ();
206+         return  Strings .hasText (parentName ) ? parentName  + "."  + field .getName () : field .getName ();
178207    }
179208
180209    public  EsField .Exact  getExactInfo () {
@@ -190,13 +219,13 @@ public FieldAttribute exactAttribute() {
190219    }
191220
192221    private  FieldAttribute  innerField (EsField  type ) {
193-         return  new  FieldAttribute (source (), this , name () + "."  + type .getName (), type , nullable (), id (), synthetic ());
222+         return  new  FieldAttribute (source (), name () , name () + "."  + type .getName (), type , nullable (), id (), synthetic ());
194223    }
195224
196225    @ Override 
197226    protected  Attribute  clone (Source  source , String  name , DataType  type , Nullability  nullability , NameId  id , boolean  synthetic ) {
198227        // Ignore `type`, this must be the same as the field's type. 
199-         return  new  FieldAttribute (source , parent , name , field , nullability , id , synthetic );
228+         return  new  FieldAttribute (source , parentName , name , field , nullability , id , synthetic );
200229    }
201230
202231    @ Override 
@@ -206,13 +235,13 @@ public Attribute withDataType(DataType type) {
206235
207236    @ Override 
208237    public  int  hashCode () {
209-         return  Objects .hash (super .hashCode (), path , field );
238+         return  Objects .hash (super .hashCode (), parentName , field );
210239    }
211240
212241    @ Override 
213242    public  boolean  equals (Object  obj ) {
214243        return  super .equals (obj )
215-             && Objects .equals (path , ((FieldAttribute ) obj ).path )
244+             && Objects .equals (parentName , ((FieldAttribute ) obj ).parentName )
216245            && Objects .equals (field , ((FieldAttribute ) obj ).field );
217246    }
218247
0 commit comments