@@ -251,7 +251,7 @@ func (r *Repository) Query(ctx context.Context, tx *connection.Tx, projectID, da
251251 // GoogleSQL for BigQuery translates a NULL array into an empty array in the query result
252252 v = []interface {}{}
253253 }
254- cell , err := r .convertValueToCell (v )
254+ cell , err := r .convertValueToCell (v , fields [ idx ] )
255255 if err != nil {
256256 return nil , fmt .Errorf ("failed to convert value to cell: %w" , err )
257257 }
@@ -307,48 +307,82 @@ func (r *Repository) queryParameterValueToGoValue(value *bigqueryv2.QueryParamet
307307 return value .Value , nil
308308}
309309
310- // zetasqlite returns [] map[string]interface{} value as struct value, also returns []interface{} value as array value.
310+ // zetasqlite returns map[string]interface{} value as struct value, also returns []interface{} value as array value.
311311// we need to convert them to specifically TableRow and TableCell type.
312- func (r * Repository ) convertValueToCell (value interface {}) (* internaltypes.TableCell , error ) {
312+ // schema provides the field ordering for struct types to ensure deterministic field order.
313+ func (r * Repository ) convertValueToCell (value interface {}, schema * bigqueryv2.TableFieldSchema ) (* internaltypes.TableCell , error ) {
313314 if value == nil {
314315 return & internaltypes.TableCell {V : nil }, nil
315316 }
316317 rv := reflect .ValueOf (value )
317318 kind := rv .Type ().Kind ()
318- if kind != reflect .Slice && kind != reflect .Array {
319- v := fmt .Sprint (value )
320- return & internaltypes.TableCell {V : v , Bytes : int64 (len (v ))}, nil
321- }
322- elemType := rv .Type ().Elem ()
323- if elemType .Kind () == reflect .Map {
319+ if kind == reflect .Map {
324320 // value is struct type
325321 var (
326322 cells []* internaltypes.TableCell
327323 totalBytes int64
328324 )
329- for i := 0 ; i < rv .Len (); i ++ {
330- fieldV := rv .Index (i )
331- keys := fieldV .MapKeys ()
332- if len (keys ) != 1 {
333- return nil , fmt .Errorf ("unexpected key number of field map value. expected 1 but got %d" , len (keys ))
325+
326+ // Build a map of field values for quick lookup
327+ fieldValues := make (map [string ]reflect.Value )
328+ keys := rv .MapKeys ()
329+ for _ , key := range keys {
330+ fieldValues [key .Interface ().(string )] = rv .MapIndex (key )
331+ }
332+
333+ // Process fields in schema order to ensure deterministic ordering
334+ // (Go map iteration order is randomized)
335+ if schema != nil && schema .Fields != nil {
336+ for _ , fieldSchema := range schema .Fields {
337+ fieldValue , exists := fieldValues [fieldSchema .Name ]
338+ if ! exists {
339+ // Field not present in data, skip it
340+ continue
341+ }
342+ cell , err := r .convertValueToCell (fieldValue .Interface (), fieldSchema )
343+ if err != nil {
344+ return nil , err
345+ }
346+ cell .Name = fieldSchema .Name
347+ totalBytes += cell .Bytes
348+ cells = append (cells , cell )
334349 }
335- cell , err := r .convertValueToCell (fieldV .MapIndex (keys [0 ]).Interface ())
336- if err != nil {
337- return nil , err
350+ } else {
351+ // Fallback: no schema available, process in arbitrary order
352+ for _ , key := range keys {
353+ cell , err := r .convertValueToCell (rv .MapIndex (key ).Interface (), nil )
354+ if err != nil {
355+ return nil , err
356+ }
357+ cell .Name = key .Interface ().(string )
358+ totalBytes += cell .Bytes
359+ cells = append (cells , cell )
338360 }
339- cell .Name = keys [0 ].Interface ().(string )
340- totalBytes += cell .Bytes
341- cells = append (cells , cell )
342361 }
343362 return & internaltypes.TableCell {V : internaltypes.TableRow {F : cells }, Bytes : totalBytes }, nil
344363 }
364+ if kind != reflect .Slice && kind != reflect .Array {
365+ v := fmt .Sprint (value )
366+ return & internaltypes.TableCell {V : v , Bytes : int64 (len (v ))}, nil
367+ }
345368 // array type
346369 var (
347370 cells = []* internaltypes.TableCell {}
348371 totalBytes int64 = 0
349372 )
373+ // For array elements, schema.Type will be the element type (e.g., STRUCT for array of structs)
374+ // and schema.Fields will contain the struct fields
375+ var elementSchema * bigqueryv2.TableFieldSchema
376+ if schema != nil {
377+ elementSchema = & bigqueryv2.TableFieldSchema {
378+ Name : schema .Name ,
379+ Type : schema .Type ,
380+ Mode : "NULLABLE" , // Array elements can be nullable
381+ Fields : schema .Fields ,
382+ }
383+ }
350384 for i := 0 ; i < rv .Len (); i ++ {
351- cell , err := r .convertValueToCell (rv .Index (i ).Interface ())
385+ cell , err := r .convertValueToCell (rv .Index (i ).Interface (), elementSchema )
352386 if err != nil {
353387 return nil , err
354388 }
0 commit comments