12
12
// See the License for the specific language governing permissions and
13
13
// limitations under the License.
14
14
15
+ use std:: collections:: HashSet ;
16
+
15
17
use anyhow:: Context ;
16
18
use itertools:: Itertools ;
17
19
use prost_reflect:: { Cardinality , FieldDescriptor , Kind , MessageDescriptor , ReflectMessage , Value } ;
@@ -26,14 +28,22 @@ use thiserror_ext::Macro;
26
28
27
29
use crate :: decoder:: { uncategorized, AccessError , AccessResult } ;
28
30
31
+ pub const PROTOBUF_MESSAGES_AS_JSONB : & str = "messages_as_jsonb" ;
32
+
29
33
pub fn pb_schema_to_column_descs (
30
34
message_descriptor : & MessageDescriptor ,
35
+ messages_as_jsonb : & HashSet < String > ,
31
36
) -> anyhow:: Result < Vec < ColumnDesc > > {
32
37
let mut columns = Vec :: with_capacity ( message_descriptor. fields ( ) . len ( ) ) ;
33
38
let mut index = 0 ;
34
39
let mut parse_trace: Vec < String > = vec ! [ ] ;
35
40
for field in message_descriptor. fields ( ) {
36
- columns. push ( pb_field_to_col_desc ( & field, & mut index, & mut parse_trace) ?) ;
41
+ columns. push ( pb_field_to_col_desc (
42
+ & field,
43
+ & mut index,
44
+ & mut parse_trace,
45
+ messages_as_jsonb,
46
+ ) ?) ;
37
47
}
38
48
39
49
Ok ( columns)
@@ -44,15 +54,18 @@ fn pb_field_to_col_desc(
44
54
field_descriptor : & FieldDescriptor ,
45
55
index : & mut i32 ,
46
56
parse_trace : & mut Vec < String > ,
57
+ messages_as_jsonb : & HashSet < String > ,
47
58
) -> anyhow:: Result < ColumnDesc > {
48
- let field_type = protobuf_type_mapping ( field_descriptor, parse_trace)
59
+ let field_type = protobuf_type_mapping ( field_descriptor, parse_trace, messages_as_jsonb )
49
60
. context ( "failed to map protobuf type" ) ?;
50
- if let Kind :: Message ( m) = field_descriptor. kind ( ) {
61
+ if let Kind :: Message ( m) = field_descriptor. kind ( )
62
+ && !messages_as_jsonb. contains ( m. full_name ( ) )
63
+ {
51
64
let field_descs = if let DataType :: List { .. } = field_type {
52
65
vec ! [ ]
53
66
} else {
54
67
m. fields ( )
55
- . map ( |f| pb_field_to_col_desc ( & f, index, parse_trace) )
68
+ . map ( |f| pb_field_to_col_desc ( & f, index, parse_trace, messages_as_jsonb ) )
56
69
. try_collect ( ) ?
57
70
} ;
58
71
* index += 1 ;
@@ -92,10 +105,12 @@ fn detect_loop_and_push(
92
105
let identifier = format ! ( "{}({})" , fd. name( ) , fd. full_name( ) ) ;
93
106
if trace. iter ( ) . any ( |s| s == identifier. as_str ( ) ) {
94
107
bail_protobuf_type_error ! (
95
- "circular reference detected: {}, conflict with {}, kind {:?}" ,
108
+ "circular reference detected: {}, conflict with {}, kind {:?}. Adding {:?} to {:?} may help. " ,
96
109
trace. iter( ) . format( "->" ) ,
97
110
identifier,
98
111
fd. kind( ) ,
112
+ fd. kind( ) ,
113
+ PROTOBUF_MESSAGES_AS_JSONB ,
99
114
) ;
100
115
}
101
116
trace. push ( identifier) ;
@@ -106,6 +121,7 @@ pub fn from_protobuf_value<'a>(
106
121
field_desc : & FieldDescriptor ,
107
122
value : & ' a Value ,
108
123
type_expected : & DataType ,
124
+ messages_as_jsonb : & ' a HashSet < String > ,
109
125
) -> AccessResult < DatumCow < ' a > > {
110
126
let kind = field_desc. kind ( ) ;
111
127
@@ -136,7 +152,7 @@ pub fn from_protobuf_value<'a>(
136
152
ScalarImpl :: Utf8 ( enum_symbol. name ( ) . into ( ) )
137
153
}
138
154
Value :: Message ( dyn_msg) => {
139
- if dyn_msg. descriptor ( ) . full_name ( ) == "google.protobuf.Any" {
155
+ if messages_as_jsonb . contains ( dyn_msg. descriptor ( ) . full_name ( ) ) {
140
156
ScalarImpl :: Jsonb ( JsonbVal :: from (
141
157
serde_json:: to_value ( dyn_msg) . map_err ( AccessError :: ProtobufAnyToJson ) ?,
142
158
) )
@@ -159,8 +175,13 @@ pub fn from_protobuf_value<'a>(
159
175
} ;
160
176
let value = dyn_msg. get_field ( & field_desc) ;
161
177
rw_values. push (
162
- from_protobuf_value ( & field_desc, & value, expected_field_type) ?
163
- . to_owned_datum ( ) ,
178
+ from_protobuf_value (
179
+ & field_desc,
180
+ & value,
181
+ expected_field_type,
182
+ messages_as_jsonb,
183
+ ) ?
184
+ . to_owned_datum ( ) ,
164
185
) ;
165
186
}
166
187
ScalarImpl :: Struct ( StructValue :: new ( rw_values) )
@@ -176,7 +197,12 @@ pub fn from_protobuf_value<'a>(
176
197
} ;
177
198
let mut builder = element_type. create_array_builder ( values. len ( ) ) ;
178
199
for value in values {
179
- builder. append ( from_protobuf_value ( field_desc, value, element_type) ?) ;
200
+ builder. append ( from_protobuf_value (
201
+ field_desc,
202
+ value,
203
+ element_type,
204
+ messages_as_jsonb,
205
+ ) ?) ;
180
206
}
181
207
ScalarImpl :: List ( ListValue :: new ( builder. finish ( ) ) )
182
208
}
@@ -209,11 +235,13 @@ pub fn from_protobuf_value<'a>(
209
235
& map_desc. map_entry_key_field ( ) ,
210
236
& key. clone ( ) . into ( ) ,
211
237
map_type. key ( ) ,
238
+ messages_as_jsonb,
212
239
) ?) ;
213
240
value_builder. append ( from_protobuf_value (
214
241
& map_desc. map_entry_value_field ( ) ,
215
242
value,
216
243
map_type. value ( ) ,
244
+ messages_as_jsonb,
217
245
) ?) ;
218
246
}
219
247
let keys = key_builder. finish ( ) ;
@@ -231,6 +259,7 @@ pub fn from_protobuf_value<'a>(
231
259
fn protobuf_type_mapping (
232
260
field_descriptor : & FieldDescriptor ,
233
261
parse_trace : & mut Vec < String > ,
262
+ messages_as_jsonb : & HashSet < String > ,
234
263
) -> std:: result:: Result < DataType , ProtobufTypeError > {
235
264
detect_loop_and_push ( parse_trace, field_descriptor) ?;
236
265
let mut t = match field_descriptor. kind ( ) {
@@ -245,20 +274,33 @@ fn protobuf_type_mapping(
245
274
Kind :: Uint64 | Kind :: Fixed64 => DataType :: Decimal ,
246
275
Kind :: String => DataType :: Varchar ,
247
276
Kind :: Message ( m) => {
248
- if m. full_name ( ) == "google.protobuf.Any" {
277
+ if messages_as_jsonb . contains ( m. full_name ( ) ) {
249
278
// Well-Known Types are identified by their full name
250
279
DataType :: Jsonb
251
280
} else if m. is_map_entry ( ) {
252
281
// Map is equivalent to `repeated MapFieldEntry map_field = N;`
253
282
debug_assert ! ( field_descriptor. is_map( ) ) ;
254
- let key = protobuf_type_mapping ( & m. map_entry_key_field ( ) , parse_trace) ?;
255
- let value = protobuf_type_mapping ( & m. map_entry_value_field ( ) , parse_trace) ?;
283
+ let key = protobuf_type_mapping (
284
+ & m. map_entry_key_field ( ) ,
285
+ parse_trace,
286
+ messages_as_jsonb,
287
+ ) ?;
288
+ let value = protobuf_type_mapping (
289
+ & m. map_entry_value_field ( ) ,
290
+ parse_trace,
291
+ messages_as_jsonb,
292
+ ) ?;
256
293
_ = parse_trace. pop ( ) ;
257
294
return Ok ( DataType :: Map ( MapType :: from_kv ( key, value) ) ) ;
258
295
} else {
259
296
let fields = m
260
297
. fields ( )
261
- . map ( |f| Ok ( ( f. name ( ) . to_owned ( ) , protobuf_type_mapping ( & f, parse_trace) ?) ) )
298
+ . map ( |f| {
299
+ Ok ( (
300
+ f. name ( ) . to_owned ( ) ,
301
+ protobuf_type_mapping ( & f, parse_trace, messages_as_jsonb) ?,
302
+ ) )
303
+ } )
262
304
. try_collect :: < _ , Vec < _ > , _ > ( ) ?;
263
305
StructType :: new ( fields) . into ( )
264
306
}
0 commit comments