6
6
*/
7
7
package org .elasticsearch .xpack .esql .core .expression ;
8
8
9
+ import org .elasticsearch .TransportVersions ;
9
10
import org .elasticsearch .common .Strings ;
10
11
import org .elasticsearch .common .io .stream .NamedWriteableRegistry ;
11
12
import org .elasticsearch .common .io .stream .StreamInput ;
12
13
import org .elasticsearch .common .io .stream .StreamOutput ;
14
+ import org .elasticsearch .core .Nullable ;
13
15
import org .elasticsearch .xpack .esql .core .tree .NodeInfo ;
14
16
import org .elasticsearch .xpack .esql .core .tree .Source ;
15
17
import org .elasticsearch .xpack .esql .core .type .DataType ;
16
18
import org .elasticsearch .xpack .esql .core .type .EsField ;
17
19
import org .elasticsearch .xpack .esql .core .util .PlanStreamInput ;
18
20
import org .elasticsearch .xpack .esql .core .util .PlanStreamOutput ;
19
- import org .elasticsearch .xpack .esql .core .util .StringUtils ;
20
21
21
22
import java .io .IOException ;
22
23
import java .util .Objects ;
23
24
25
+ import static org .elasticsearch .xpack .esql .core .util .PlanStreamInput .readCachedStringWithVersionCheck ;
26
+ import static org .elasticsearch .xpack .esql .core .util .PlanStreamOutput .writeCachedStringWithVersionCheck ;
27
+
24
28
/**
25
29
* Attribute for an ES field.
26
30
* To differentiate between the different type of fields this class offers:
@@ -37,32 +41,31 @@ public class FieldAttribute extends TypedAttribute {
37
41
FieldAttribute ::readFrom
38
42
);
39
43
40
- private final FieldAttribute parent ;
41
- private final String path ;
44
+ private final String parentName ;
42
45
private final EsField field ;
43
46
44
47
public FieldAttribute (Source source , String name , EsField field ) {
45
48
this (source , null , name , field );
46
49
}
47
50
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 );
50
53
}
51
54
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 );
54
57
}
55
58
56
59
public FieldAttribute (
57
60
Source source ,
58
- FieldAttribute parent ,
61
+ @ Nullable String parentName ,
59
62
String name ,
60
63
EsField field ,
61
64
Nullability nullability ,
62
- NameId id ,
65
+ @ Nullable NameId id ,
63
66
boolean synthetic
64
67
) {
65
- this (source , parent , name , field .getDataType (), field , nullability , id , synthetic );
68
+ this (source , parentName , name , field .getDataType (), field , nullability , id , synthetic );
66
69
}
67
70
68
71
/**
@@ -71,17 +74,16 @@ public FieldAttribute(
71
74
*/
72
75
FieldAttribute (
73
76
Source source ,
74
- FieldAttribute parent ,
77
+ @ Nullable String parentName ,
75
78
String name ,
76
79
DataType type ,
77
80
EsField field ,
78
81
Nullability nullability ,
79
- NameId id ,
82
+ @ Nullable NameId id ,
80
83
boolean synthetic
81
84
) {
82
85
super (source , name , type , nullability , id , synthetic );
83
- this .path = parent != null ? parent .name () : StringUtils .EMPTY ;
84
- this .parent = parent ;
86
+ this .parentName = parentName ;
85
87
this .field = field ;
86
88
}
87
89
@@ -91,16 +93,16 @@ public FieldAttribute(
91
93
*/
92
94
private FieldAttribute (
93
95
Source source ,
94
- FieldAttribute parent ,
96
+ @ Nullable String parentName ,
95
97
String name ,
96
98
DataType type ,
97
99
EsField field ,
98
- String qualifier ,
100
+ @ Nullable String qualifier ,
99
101
Nullability nullability ,
100
- NameId id ,
102
+ @ Nullable NameId id ,
101
103
boolean synthetic
102
104
) {
103
- this (source , parent , name , type , field , nullability , id , synthetic );
105
+ this (source , parentName , name , type , field , nullability , id , synthetic );
104
106
}
105
107
106
108
private FieldAttribute (StreamInput in ) throws IOException {
@@ -114,8 +116,8 @@ private FieldAttribute(StreamInput in) throws IOException {
114
116
*/
115
117
this (
116
118
Source .readFrom ((StreamInput & PlanStreamInput ) in ),
117
- in . readOptionalWriteable ( FieldAttribute :: readFrom ),
118
- (( PlanStreamInput ) in ). readCachedString ( ),
119
+ readParentName ( in ),
120
+ readCachedStringWithVersionCheck ( in ),
119
121
DataType .readFrom (in ),
120
122
EsField .readFrom (in ),
121
123
in .readOptionalString (),
@@ -129,8 +131,8 @@ private FieldAttribute(StreamInput in) throws IOException {
129
131
public void writeTo (StreamOutput out ) throws IOException {
130
132
if (((PlanStreamOutput ) out ).writeAttributeCacheHeader (this )) {
131
133
Source .EMPTY .writeTo (out );
132
- out . writeOptionalWriteable ( parent );
133
- (( PlanStreamOutput ) out ). writeCachedString ( name ());
134
+ writeParentName ( out );
135
+ writeCachedStringWithVersionCheck ( out , name ());
134
136
dataType ().writeTo (out );
135
137
field .writeTo (out );
136
138
// 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 {
145
147
return ((PlanStreamInput ) in ).readAttributeWithCache (FieldAttribute ::new );
146
148
}
147
149
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
+
148
170
@ Override
149
171
public String getWriteableName () {
150
172
return ENTRY .name ;
151
173
}
152
174
153
175
@ Override
154
176
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
+ );
160
189
}
161
190
162
- public String path () {
163
- return path ;
191
+ public String parentName () {
192
+ return parentName ;
164
193
}
165
194
166
195
/**
@@ -174,7 +203,7 @@ public String fieldName() {
174
203
if ((synthetic () || name ().startsWith (SYNTHETIC_ATTRIBUTE_NAME_PREFIX )) == false ) {
175
204
return name ();
176
205
}
177
- return Strings .hasText (path ) ? path + "." + field .getName () : field .getName ();
206
+ return Strings .hasText (parentName ) ? parentName + "." + field .getName () : field .getName ();
178
207
}
179
208
180
209
public EsField .Exact getExactInfo () {
@@ -190,13 +219,13 @@ public FieldAttribute exactAttribute() {
190
219
}
191
220
192
221
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 ());
194
223
}
195
224
196
225
@ Override
197
226
protected Attribute clone (Source source , String name , DataType type , Nullability nullability , NameId id , boolean synthetic ) {
198
227
// 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 );
200
229
}
201
230
202
231
@ Override
@@ -206,13 +235,13 @@ public Attribute withDataType(DataType type) {
206
235
207
236
@ Override
208
237
public int hashCode () {
209
- return Objects .hash (super .hashCode (), path , field );
238
+ return Objects .hash (super .hashCode (), parentName , field );
210
239
}
211
240
212
241
@ Override
213
242
public boolean equals (Object obj ) {
214
243
return super .equals (obj )
215
- && Objects .equals (path , ((FieldAttribute ) obj ).path )
244
+ && Objects .equals (parentName , ((FieldAttribute ) obj ).parentName )
216
245
&& Objects .equals (field , ((FieldAttribute ) obj ).field );
217
246
}
218
247
0 commit comments