Repository: snowflakedb/gosnowflake
Pull request #1096 (2024-04-10 15:44:01)

SNOW-1283946 Embed structured types
### Description

SNOW-1283946 Handle embedded structured types.

### Checklist
- [ ] Created tests which fail without the change (if possible)
- [ ] Extended the README / documentation, if necessary



Patch:
diff --git a/converter.go b/converter.go
index beb9154..c000697 100644
--- a/converter.go
+++ b/converter.go
@@ -349,12 +349,15 @@ func stringToValue(ctx context.Context, dest *driver.Value, srcColumnMeta execRe
 			return nil
 		}
 		m := make(map[string]any)
-		decoder := json.NewDecoder(bytes.NewBufferString(*srcValue))
-		decoder.UseNumber()
+		decoder := decoderWithNumbersAsStrings(srcValue)
 		if err := decoder.Decode(&m); err != nil {
 			return err
 		}
-		*dest = buildStructuredTypeRecursive(m, srcColumnMeta.Fields, params)
+		v, err := buildStructuredTypeRecursive(ctx, m, srcColumnMeta.Fields, params)
+		if err != nil {
+			return err
+		}
+		*dest = v
 		return nil
 	case "text", "fixed", "real", "variant":
 		*dest = *srcValue
@@ -437,7 +440,12 @@ func stringToValue(ctx context.Context, dest *driver.Value, srcColumnMeta execRe
 		if len(srcColumnMeta.Fields) > 1 {
 			return errors.New("got more than one field for array")
 		}
-		v, err := buildStructuredArray(srcColumnMeta.Fields[0], srcValue, params)
+		var arr []any
+		decoder := decoderWithNumbersAsStrings(srcValue)
+		if err := decoder.Decode(&arr); err != nil {
+			return err
+		}
+		v, err := buildStructuredArray(ctx, srcColumnMeta.Fields[0], arr, params)
 		if err != nil {
 			return err
 		}
@@ -445,247 +453,324 @@ func stringToValue(ctx context.Context, dest *driver.Value, srcColumnMeta execRe
 		return nil
 	case "map":
 		var err error
-		*dest, err = jsonToMap(ctx, srcColumnMeta, *srcValue, params)
+		*dest, err = jsonToMap(ctx, srcColumnMeta.Fields[0], srcColumnMeta.Fields[1], *srcValue, params)
 		return err
 	}
 	*dest = *srcValue
 	return nil
 }
 
-func jsonToMap(ctx context.Context, srcColumnMeta execResponseRowType, srcValue string, params map[string]*string) (snowflakeValue, error) {
-	keyType := srcColumnMeta.Fields[0].Type
-	switch keyType {
+func jsonToMap(ctx context.Context, keyMetadata, valueMetadata fieldMetadata, srcValue string, params map[string]*string) (snowflakeValue, error) {
+	switch keyMetadata.Type {
 	case "text":
-		return jsonToMapWithKeyType[string](ctx, srcColumnMeta, srcValue, params)
+		var m map[string]any
+		decoder := decoderWithNumbersAsStrings(&srcValue)
+		err := decoder.Decode(&m)
+		if err != nil {
+			return nil, err
+		}
+		// returning snowflakeValue of complex types does not work with generics
+		if valueMetadata.Type == "object" {
+			res := make(map[string]*structuredType)
+			for k, v := range m {
+				res[k] = buildStructuredTypeFromMap(v.(map[string]any), valueMetadata.Fields, params)
+			}
+			return res, nil
+		}
+		return jsonToMapWithKeyType[string](ctx, valueMetadata, m, params)
 	case "fixed":
-		return jsonToMapWithKeyType[int64](ctx, srcColumnMeta, srcValue, params)
+		var m map[int64]any
+		decoder := decoderWithNumbersAsStrings(&srcValue)
+		err := decoder.Decode(&m)
+		if err != nil {
+			return nil, err
+		}
+		if valueMetadata.Type == "object" {
+			res := make(map[int64]*structuredType)
+			for k, v := range m {
+				res[k] = buildStructuredTypeFromMap(v.(map[string]any), valueMetadata.Fields, params)
+			}
+			return res, nil
+		}
+		return jsonToMapWithKeyType[int64](ctx, valueMetadata, m, params)
 	default:
-		return nil, fmt.Errorf("unsupported map key type: %v", keyType)
-	}
-}
-
-func ifNotNullOrDefault[T any](t *T, def T) T {
-	if t == nil {
-		return def
+		return nil, fmt.Errorf("unsupported map key type: %v", keyMetadata.Type)
 	}
-	return *t
 }
 
-func jsonToMapWithKeyType[K comparable](ctx context.Context, srcColumnMeta execResponseRowType, srcValue string, params map[string]*string) (snowflakeValue, error) {
+func jsonToMapWithKeyType[K comparable](ctx context.Context, valueMetadata fieldMetadata, m map[K]any, params map[string]*string) (snowflakeValue, error) {
 	mapValuesNullableEnabled := mapValuesNullableEnabled(ctx)
-	valueMetadata := srcColumnMeta.Fields[1]
 	switch valueMetadata.Type {
 	case "text":
-		if mapValuesNullableEnabled {
-			return buildNullValue[K](srcValue, func(v *string) sql.NullString {
-				return sql.NullString{Valid: v != nil, String: ifNotNullOrDefault(v, "")}
-			})
-		}
-		return unmarshallMap(srcValue, make(map[K]string))
+		return buildMapValues[K, sql.NullString, string](mapValuesNullableEnabled, m, func(v any) (string, error) {
+			return v.(string), nil
+		}, func(v any) (sql.NullString, error) {
+			return sql.NullString{Valid: v != nil, String: ifNotNullOrDefault(v, "")}, nil
+		})
 	case "boolean":
-		if mapValuesNullableEnabled {
-			return buildNullValue[K](srcValue, func(v *bool) sql.NullBool {
-				return sql.NullBool{Valid: v != nil, Bool: ifNotNullOrDefault(v, false)}
-			})
-		}
-		return unmarshallMap(srcValue, make(map[K]bool))
+		return buildMapValues[K, sql.NullBool, bool](mapValuesNullableEnabled, m, func(v any) (bool, error) {
+			return v.(bool), nil
+		}, func(v any) (sql.NullBool, error) {
+			return sql.NullBool{Valid: v != nil, Bool: ifNotNullOrDefault(v, false)}, nil
+		})
 	case "fixed":
 		if valueMetadata.Scale == 0 {
-			if mapValuesNullableEnabled {
-				return buildNullValue[K](srcValue, func(v *int64) sql.NullInt64 {
-					return sql.NullInt64{Valid: v != nil, Int64: ifNotNullOrDefault(v, 0)}
-				})
-			}
-			return unmarshallMap(srcValue, make(map[K]int64))
-		}
-		if mapValuesNullableEnabled {
-			return buildNullValue[K](srcValue, func(v *float64) sql.NullFloat64 {
-				return sql.NullFloat64{Valid: v != nil, Float64: ifNotNullOrDefault(v, 0)}
+			return buildMapValues[K, sql.NullInt64, int64](mapValuesNullableEnabled, m, func(v any) (int64, error) {
+				return strconv.ParseInt(string(v.(json.Number)), 10, 64)
+			}, func(v any) (sql.NullInt64, error) {
+				if v != nil {
+					i64, err := strconv.ParseInt(string(v.(json.Number)), 10, 64)
+					if err != nil {
+						return sql.NullInt64{}, err
+					}
+					return sql.NullInt64{Valid: true, Int64: i64}, nil
+				}
+				return sql.NullInt64{Valid: false}, nil
 			})
 		}
-		return unmarshallMap(srcValue, make(map[K]float64))
+		return buildMapValues[K, sql.NullFloat64, float64](mapValuesNullableEnabled, m, func(v any) (float64, error) {
+			return strconv.ParseFloat(string(v.(json.Number)), 64)
+		}, func(v any) (sql.NullFloat64, error) {
+			if v != nil {
+				f64, err := strconv.ParseFloat(string(v.(json.Number)), 64)
+				if err != nil {
+					return sql.NullFloat64{}, err
+				}
+				return sql.NullFloat64{Valid: true, Float64: f64}, nil
+			}
+			return sql.NullFloat64{Valid: false}, nil
+		})
 	case "real":
-		if mapValuesNullableEnabled {
-			return buildNullValue[K](srcValue, func(v *float64) sql.NullFloat64 {
-				return sql.NullFloat64{Valid: v != nil, Float64: ifNotNullOrDefault(v, 0)}
-			})
-		}
-		return unmarshallMap(srcValue, make(map[K]float64))
-	case "binary":
-		stringMap, err := unmarshallMap(srcValue, make(map[K]*string))
-		if err != nil {
-			return nil, err
-		}
-		bytesMap := make(map[K][]byte, len(stringMap))
-		for k, v := range stringMap {
-			if v == nil {
-				bytesMap[k] = nil
-			} else {
-				var err error
-				bytesMap[k], err = hex.DecodeString(*v)
+		return buildMapValues[K, sql.NullFloat64, float64](mapValuesNullableEnabled, m, func(v any) (float64, error) {
+			return strconv.ParseFloat(string(v.(json.Number)), 64)
+		}, func(v any) (sql.NullFloat64, error) {
+			if v != nil {
+				f64, err := strconv.ParseFloat(string(v.(json.Number)), 64)
 				if err != nil {
-					return nil, err
+					return sql.NullFloat64{}, err
 				}
+				return sql.NullFloat64{Valid: true, Float64: f64}, nil
 			}
+			return sql.NullFloat64{Valid: false}, nil
+		})
+	case "binary":
+		return buildMapValues[K, []byte, []byte](mapValuesNullableEnabled, m, func(v any) ([]byte, error) {
+			return hex.DecodeString(v.(string))
+		}, func(v any) ([]byte, error) {
+			if v == nil {
+				return nil, nil
+			}
+			return hex.DecodeString(v.(string))
+		})
+	case "date", "time", "timestamp_tz", "timestamp_ltz", "timestamp_ntz":
+		return buildMapValues[K, sql.NullTime, time.Time](mapValuesNullableEnabled, m, func(v any) (time.Time, error) {
+			sfFormat, err := dateTimeFormatByType(valueMetadata.Type, params)
+			if err != nil {
+				return time.Time{}, err
+			}
+			goFormat, err := snowflakeFormatToGoFormat(sfFormat)
+			if err != nil {
+				return time.Time{}, err
+			}
+			return time.Parse(goFormat, v.(string))
+		}, func(v any) (sql.NullTime, error) {
+			if v == nil {
+				return sql.NullTime{Valid: false}, nil
+			}
+			sfFormat, err := dateTimeFormatByType(valueMetadata.Type, params)
+			if err != nil {
+				return sql.NullTime{}, err
+			}
+			goFormat, err := snowflakeFormatToGoFormat(sfFormat)
+			if err != nil {
+				return sql.NullTime{}, err
+			}
+			time, err := time.Parse(goFormat, v.(string))
+			if err != nil {
+				return sql.NullTime{}, err
+			}
+			return sql.NullTime{Valid: true, Time: time}, nil
+		})
+	case "array":
+		arrayMetadata := valueMetadata.Fields[0]
+		switch arrayMetadata.Type {
+		case "text":
+			return buildArrayFromMap[K, string](ctx, arrayMetadata, m, params)
+		case "fixed":
+			if arrayMetadata.Scale == 0 {
+				return buildArrayFromMap[K, int64](ctx, arrayMetadata, m, params)
+			}
+			return buildArrayFromMap[K, float64](ctx, arrayMetadata, m, params)
+		case "real":
+			return buildArrayFromMap[K, float64](ctx, arrayMetadata, m, params)
+		case "binary":
+			return buildArrayFromMap[K, []byte](ctx, arrayMetadata, m, params)
+		case "boolean":
+			return buildArrayFromMap[K, bool](ctx, arrayMetadata, m, params)
+		case "date", "time", "timestamp_ltz", "timestamp_tz", "timestamp_ntz":
+			return buildArrayFromMap[K, time.Time](ctx, arrayMetadata, m, params)
 		}
-		return bytesMap, nil
-	case "date", "time", "timestamp_ntz", "timestamp_ltz", "timestamp_tz":
-		stringMap, err := unmarshallMap(srcValue, make(map[K]*string))
-		if err != nil {
-			return nil, err
-		}
-		sfFormat, err := dateTimeFormatByType(valueMetadata.Type, params)
-		if err != nil {
-			return nil, err
-		}
-		goFormat, err := snowflakeFormatToGoFormat(sfFormat)
+	}
+	return nil, fmt.Errorf("unsupported map value type: %v", valueMetadata.Type)
+}
+
+func buildArrayFromMap[K comparable, V any](ctx context.Context, valueMetadata fieldMetadata, m map[K]any, params map[string]*string) (snowflakeValue, error) {
+	res := make(map[K][]V)
+	for k, v := range m {
+		structuredArray, err := buildStructuredArray(ctx, valueMetadata, v.([]any), params)
 		if err != nil {
 			return nil, err
 		}
-		if mapValuesNullableEnabled {
-			dateTimeMap := make(map[K]sql.NullTime, len(stringMap))
-			for k, v := range stringMap {
-				if v == nil {
-					dateTimeMap[k] = sql.NullTime{Valid: false}
-				} else {
-					t, err := time.Parse(goFormat, *v)
-					if err != nil {
-						return nil, err
-					}
-					dateTimeMap[k] = sql.NullTime{Valid: true, Time: t}
-				}
-			}
-			return dateTimeMap, nil
-		}
-		dateTimeMap := make(map[K]time.Time, len(stringMap))
-		for k, v := range stringMap {
-			if dateTimeMap[k], err = time.Parse(goFormat, *v); err != nil {
-				return nil, err
-			}
-		}
-		return dateTimeMap, nil
-	default:
-		return nil, fmt.Errorf("unsupported map value type: %v", valueMetadata.Type)
+		res[k] = structuredArray.([]V)
 	}
+	return res, nil
 }
 
-func buildNullValue[K comparable, V any, nullV any](srcValue string, buildNullV func(v *V) nullV) (snowflakeValue, error) {
-	vMap, err := unmarshallMap(srcValue, make(map[K]*V))
-	if err != nil {
-		return nil, err
+func buildStructuredTypeFromMap(values map[string]any, fieldMetadata []fieldMetadata, params map[string]*string) *structuredType {
+	return &structuredType{
+		values:        values,
+		params:        params,
+		fieldMetadata: fieldMetadata,
 	}
-	nullVMap := make(map[K]nullV, len(vMap))
-	for k, v := range vMap {
-		nullVMap[k] = buildNullV(v)
+}
+
+func ifNotNullOrDefault[T any](t any, def T) T {
+	if t == nil {
+		return def
 	}
-	return nullVMap, nil
+	return t.(T)
 }
 
-func unmarshallMap[K comparable, V any](srcValue string, m map[K]V) (map[K]V, error) {
-	if err := json.Unmarshal([]byte(srcValue), &m); err != nil {
-		return nil, err
+func buildMapValues[K comparable, Vnullable any, VnotNullable any](mapValuesNullableEnabled bool, m map[K]any, buildNotNullable func(v any) (VnotNullable, error), buildNullable func(v any) (Vnullable, error)) (snowflakeValue, error) {
+	var err error
+	if mapValuesNullableEnabled {
+		result := make(map[K]Vnullable, len(m))
+		for k, v := range m {
+			if result[k], err = buildNullable(v); err != nil {
+				return nil, err
+			}
+		}
+		return result, nil
 	}
-	return m, nil
+	result := make(map[K]VnotNullable, len(m))
+	for k, v := range m {
+		if result[k], err = buildNotNullable(v); err != nil {
+			return nil, err
+		}
+	}
+	return result, nil
 }
 
-func buildStructuredArray(fieldMetadata fieldMetadata, srcValue *string, params map[string]*string) (any, error) {
+func buildStructuredArray(ctx context.Context, fieldMetadata fieldMetadata, srcValue []any, params map[string]*string) (any, error) {
 	switch fieldMetadata.Type {
 	case "text":
-		var v []string
-		err := json.Unmarshal([]byte(*srcValue), &v)
-		if err != nil {
-			return nil, err
-		}
-		return v, nil
+		return copyArrayAndConvert[string](srcValue, func(input any) (string, error) {
+			return input.(string), nil
+		})
 	case "fixed":
 		if fieldMetadata.Scale == 0 {
-			var v []int64
-			err := json.Unmarshal([]byte(*srcValue), &v)
-			if err != nil {
-				return nil, err
-			}
-			return v, nil
-		}
-		var v []float64
-		err := json.Unmarshal([]byte(*srcValue), &v)
-		if err != nil {
-			return nil, err
+			return copyArrayAndConvert[int64](srcValue, func(input any) (int64, error) {
+				return strconv.ParseInt(string(input.(json.Number)), 10, 64)
+			})
 		}
-		return v, nil
+		return copyArrayAndConvert[float64](srcValue, func(input any) (float64, error) {
+			return strconv.ParseFloat(string(input.(json.Number)), 64)
+		})
 	case "real":
-		var v []float64
-		err := json.Unmarshal([]byte(*srcValue), &v)
-		if err != nil {
-			return nil, err
-		}
-		return v, nil
+		return copyArrayAndConvert[float64](srcValue, func(input any) (float64, error) {
+			return strconv.ParseFloat(string(input.(json.Number)), 64)
+		})
 	case "time", "date", "timestamp_ltz", "timestamp_ntz", "timestamp_tz":
-		var strings []string
-		err := json.Unmarshal([]byte(*srcValue), &strings)
-		if err != nil {
-			return nil, err
-		}
-		v := make([]time.Time, len(strings))
-		sfFormat, err := dateTimeFormatByType(fieldMetadata.Type, params)
-		if err != nil {
-			return nil, err
-		}
-		goFormat, err := snowflakeFormatToGoFormat(sfFormat)
-		if err != nil {
-			return nil, err
-		}
-		for i, s := range strings {
-			if v[i], err = time.Parse(goFormat, s); err != nil {
-				return nil, err
+		return copyArrayAndConvert[time.Time](srcValue, func(input any) (time.Time, error) {
+			sfFormat, err := dateTimeFormatByType(fieldMetadata.Type, params)
+			if err != nil {
+				return time.Time{}, err
 			}
-		}
-		return v, nil
+			goFormat, err := snowflakeFormatToGoFormat(sfFormat)
+			if err != nil {
+				return time.Time{}, err
+			}
+			return time.Parse(goFormat, input.(string))
+		})
 	case "boolean":
-		var v []bool
-		err := json.Unmarshal([]byte(*srcValue), &v)
-		if err != nil {
-			return nil, err
-		}
-		return v, nil
+		return copyArrayAndConvert[bool](srcValue, func(input any) (bool, error) {
+			return input.(bool), nil
+		})
 	case "binary":
-		var strings []string
-		err := json.Unmarshal([]byte(*srcValue), &strings)
-		if err != nil {
-			return nil, err
-		}
-		v := make([][]byte, len(strings))
-		for i, s := range strings {
-			if v[i], err = hex.DecodeString(s); err != nil {
-				return nil, err
+		return copyArrayAndConvert[[]byte](srcValue, func(input any) ([]byte, error) {
+			return hex.DecodeString(input.(string))
+		})
+	case "object":
+		return copyArrayAndConvert[*structuredType](srcValue, func(input any) (*structuredType, error) {
+			return buildStructuredTypeRecursive(ctx, input.(map[string]any), fieldMetadata.Fields, params)
+		})
+	case "array":
+		switch fieldMetadata.Fields[0].Type {
+		case "text":
+			return buildStructuredArrayRecursive[string](ctx, fieldMetadata.Fields[0], srcValue, params)
+		case "fixed":
+			if fieldMetadata.Fields[0].Scale == 0 {
+				return buildStructuredArrayRecursive[int64](ctx, fieldMetadata.Fields[0], srcValue, params)
 			}
+			return buildStructuredArrayRecursive[float64](ctx, fieldMetadata.Fields[0], srcValue, params)
+		case "real":
+			return buildStructuredArrayRecursive[float64](ctx, fieldMetadata.Fields[0], srcValue, params)
+		case "boolean":
+			return buildStructuredArrayRecursive[bool](ctx, fieldMetadata.Fields[0], srcValue, params)
+		case "binary":
+			return buildStructuredArrayRecursive[[]byte](ctx, fieldMetadata.Fields[0], srcValue, params)
+		case "date", "time", "timestamp_ltz", "timestamp_ntz", "timestamp_tz":
+			return buildStructuredArrayRecursive[time.Time](ctx, fieldMetadata.Fields[0], srcValue, params)
 		}
-		return v, nil
-	case "object":
-		var maps []map[string]any
-		err := json.Unmarshal([]byte(*srcValue), &maps)
+	}
+	return srcValue, nil
+}
+
+func buildStructuredArrayRecursive[T any](ctx context.Context, fieldMetadata fieldMetadata, srcValue []any, params map[string]*string) ([][]T, error) {
+	arr := make([][]T, len(srcValue))
+	for i, v := range srcValue {
+		structuredArray, err := buildStructuredArray(ctx, fieldMetadata, v.([]any), params)
 		if err != nil {
 			return nil, err
 		}
-		objects := make([]*structuredType, len(maps))
-		for i, m := range maps {
-			objects[i] = buildStructuredTypeRecursive(m, fieldMetadata.Fields, params)
+		arr[i] = structuredArray.([]T)
+	}
+	return arr, nil
+}
+
+func copyArrayAndConvert[T any](input []any, convertFunc func(input any) (T, error)) ([]T, error) {
+	var err error
+	output := make([]T, len(input))
+	for i, s := range input {
+		if output[i], err = convertFunc(s); err != nil {
+			return nil, err
 		}
-		return objects, nil
 	}
-	return srcValue, nil
+	return output, nil
 }
 
-func buildStructuredTypeRecursive(m map[string]any, fields []fieldMetadata, params map[string]*string) *structuredType {
+func buildStructuredTypeRecursive(ctx context.Context, m map[string]any, fields []fieldMetadata, params map[string]*string) (*structuredType, error) {
+	var err error
 	for _, fm := range fields {
-		if fm.Type == "object" && m[fm.Name] != nil {
-			m[fm.Name] = buildStructuredTypeRecursive(m[fm.Name].(map[string]any), fm.Fields, params)
+		if fm.Type == "array" && m[fm.Name] != nil {
+			if m[fm.Name], err = buildStructuredArray(ctx, fm.Fields[0], m[fm.Name].([]any), params); err != nil {
+				return nil, err
+			}
+		} else if fm.Type == "map" && m[fm.Name] != nil {
+			if m[fm.Name], err = jsonToMapWithKeyType(ctx, fm.Fields[1], m[fm.Name].(map[string]any), params); err != nil {
+				return nil, err
+			}
+		} else if fm.Type == "object" && m[fm.Name] != nil {
+			if m[fm.Name], err = buildStructuredTypeRecursive(ctx, m[fm.Name].(map[string]any), fm.Fields, params); err != nil {
+				return nil, err
+			}
 		}
 	}
 	return &structuredType{
 		values:        m,
 		fieldMetadata: fields,
 		params:        params,
-	}
+	}, nil
 }
 
 var decimalShift = new(big.Int).Exp(big.NewInt(2), big.NewInt(64), nil)
@@ -800,14 +885,14 @@ func arrowToValues(
 	var err error
 	snowflakeType := getSnowflakeType(srcColumnMeta.Type)
 	for i := range destcol {
-		if destcol[i], err = arrowToValue(ctx, i, srcColumnMeta, srcValue, loc, higherPrecision, params, snowflakeType); err != nil {
+		if destcol[i], err = arrowToValue(ctx, i, srcColumnMeta.toFieldMetadata(), srcValue, loc, higherPrecision, params, snowflakeType); err != nil {
 			return err
 		}
 	}
 	return nil
 }
 
-func arrowToValue(ctx context.Context, rowIdx int, srcColumnMeta execResponseRowType, srcValue arrow.Array, loc *time.Location, higherPrecision bool, params map[string]*string, snowflakeType snowflakeType) (snowflakeValue, error) {
+func arrowToValue(ctx context.Context, rowIdx int, srcColumnMeta fieldMetadata, srcValue arrow.Array, loc *time.Location, higherPrecision bool, params map[string]*string, snowflakeType snowflakeType) (snowflakeValue, error) {
 	switch snowflakeType {
 	case fixedType:
 		// Snowflake data types that are fixed-point numbers will fall into this category
@@ -851,98 +936,16 @@ func arrowToValue(ctx context.Context, rowIdx int, srcColumnMeta execResponseRow
 			// structured array as json
 			if !srcValue.IsNull(rowIdx) {
 				val := strings.Value(rowIdx)
-				return buildStructuredArray(srcColumnMeta.Fields[0], &val, params)
-			}
-			return nil, nil
-		}
-		list := srcValue.(*array.List)
-		if list.IsNull(rowIdx) {
-			return nil, nil
-		}
-		values := list.ListValues()
-		offsets := list.Offsets()
-		snowflakeType := getSnowflakeType(srcColumnMeta.Fields[0].Type)
-		switch snowflakeType {
-		case fixedType:
-			switch typedValues := values.(type) {
-			case *array.Decimal128:
-				if higherPrecision && srcColumnMeta.Fields[0].Scale == 0 {
-					return mapStructuredArrayNativeArrowRows(offsets, rowIdx, func(j int) (*big.Int, error) {
-						return arrowDecimal128ToValue(typedValues, j, higherPrecision, srcColumnMeta.Fields[0].Scale).(*big.Int), nil
-					})
-				} else if higherPrecision && srcColumnMeta.Fields[0].Scale != 0 {
-					return mapStructuredArrayNativeArrowRows(offsets, rowIdx, func(j int) (*big.Float, error) {
-						return arrowDecimal128ToValue(typedValues, j, higherPrecision, srcColumnMeta.Fields[0].Scale).(*big.Float), nil
-					})
-				} else if !higherPrecision && srcColumnMeta.Fields[0].Scale == 0 {
-					return mapStructuredArrayNativeArrowRows(offsets, rowIdx, func(j int) (int64, error) {
-						v := arrowDecimal128ToValue(typedValues, j, higherPrecision, srcColumnMeta.Fields[0].Scale)
-						return strconv.ParseInt(v.(string), 10, 64)
-					})
-				} else {
-					return mapStructuredArrayNativeArrowRows(offsets, rowIdx, func(j int) (float64, error) {
-						v := arrowDecimal128ToValue(typedValues, j, higherPrecision, srcColumnMeta.Fields[0].Scale)
-						return strconv.ParseFloat(v.(string), 64)
-					})
+				var arr []any
+				decoder := decoderWithNumbersAsStrings(&val)
+				if err := decoder.Decode(&arr); err != nil {
+					return nil, err
 				}
-			case *array.Int64:
-				return mapStructuredArrayNativeArrowRows(offsets, rowIdx, func(j int) (int64, error) {
-					return arrowInt64ToValue(typedValues, j, higherPrecision, srcColumnMeta.Fields[0].Scale).(int64), nil
-				})
-			case *array.Int32:
-				return mapStructuredArrayNativeArrowRows(offsets, rowIdx, func(j int) (int32, error) {
-					return arrowInt32ToValue(typedValues, j, higherPrecision, srcColumnMeta.Fields[0].Scale).(int32), nil
-				})
-			case *array.Int16:
-				return mapStructuredArrayNativeArrowRows(offsets, rowIdx, func(j int) (int16, error) {
-					return arrowInt16ToValue(typedValues, j, higherPrecision, srcColumnMeta.Fields[0].Scale).(int16), nil
-				})
-			case *array.Int8:
-				return mapStructuredArrayNativeArrowRows(offsets, rowIdx, func(j int) (int8, error) {
-					return arrowInt8ToValue(typedValues, j, higherPrecision, srcColumnMeta.Fields[0].Scale).(int8), nil
-				})
+				return buildStructuredArray(ctx, srcColumnMeta.Fields[0], arr, params)
 			}
-		case realType:
-			return mapStructuredArrayNativeArrowRows(offsets, rowIdx, func(j int) (float64, error) {
-				return arrowRealToValue(values.(*array.Float64), j).(float64), nil
-			})
-		case textType:
-			return mapStructuredArrayNativeArrowRows(offsets, rowIdx, func(j int) (string, error) {
-				return arrowStringToValue(values.(*array.String), j).(string), nil
-			})
-		case booleanType:
-			return mapStructuredArrayNativeArrowRows(offsets, rowIdx, func(j int) (bool, error) {
-				return arrowBoolToValue(values.(*array.Boolean), j).(bool), nil
-			})
-		case binaryType:
-			return mapStructuredArrayNativeArrowRows(offsets, rowIdx, func(j int) ([]byte, error) {
-				return arrowBinaryToValue(values.(*array.Binary), j).([]byte), nil
-			})
-		case dateType:
-			return mapStructuredArrayNativeArrowRows(offsets, rowIdx, func(j int) (time.Time, error) {
-				return arrowDateToValue(values.(*array.Date32), int(j)).(time.Time), nil
-			})
-		case timeType:
-			return mapStructuredArrayNativeArrowRows(offsets, rowIdx, func(j int) (time.Time, error) {
-				return arrowTimeToValue(values, int(j), srcColumnMeta.Fields[0].Scale).(time.Time), nil
-			})
-		case timestampNtzType, timestampLtzType, timestampTzType:
-			return mapStructuredArrayNativeArrowRows(offsets, rowIdx, func(j int) (time.Time, error) {
-				ptr := arrowSnowflakeTimestampToTime(values, snowflakeType, srcColumnMeta.Fields[0].Scale, int(j), loc)
-				if ptr != nil {
-					return *ptr, nil
-				}
-				return time.Time{}, nil
-			})
-		case objectType:
-			return mapStructuredArrayNativeArrowRows(offsets, rowIdx, func(j int) (*structuredType, error) {
-				m := make(map[string]any, len(srcColumnMeta.Fields[0].Fields))
-				for fieldIdx, field := range srcColumnMeta.Fields[0].Fields {
-					m[field.Name] = values.(*array.Struct).Field(fieldIdx).ValueStr(int(j))
-				}
-				return buildStructuredTypeRecursive(m, srcColumnMeta.Fields[0].Fields, params), nil
-			})
+			return nil, nil
 		}
+		return buildListFromNativeArrow(ctx, rowIdx, srcColumnMeta.Fields[0], srcValue, loc, higherPrecision, params)
 	case objectType:
 		if len(srcColumnMeta.Fields) == 0 {
 			// semistructured type without schema
@@ -957,18 +960,18 @@ func arrowToValue(ctx context.Context, rowIdx int, srcColumnMeta execResponseRow
 			// structured objects as json
 			if !srcValue.IsNull(rowIdx) {
 				m := make(map[string]any)
-				decoder := json.NewDecoder(bytes.NewBufferString(strings.Value(rowIdx)))
-				decoder.UseNumber()
+				value := strings.Value(rowIdx)
+				decoder := decoderWithNumbersAsStrings(&value)
 				if err := decoder.Decode(&m); err != nil {
 					return nil, err
 				}
-				return buildStructuredTypeRecursive(m, srcColumnMeta.Fields, params), nil
+				return buildStructuredTypeRecursive(ctx, m, srcColumnMeta.Fields, params)
 			}
 			return nil, nil
 		}
 		// structured objects as native arrow
 		structs := srcValue.(*array.Struct)
-		return arrowToStructuredType(structs, srcColumnMeta.Fields, loc, rowIdx, higherPrecision, params), nil
+		return arrowToStructuredType(ctx, structs, srcColumnMeta.Fields, loc, rowIdx, higherPrecision, params)
 	case mapType:
 		if srcValue.IsNull(rowIdx) {
 			return nil, nil
@@ -977,33 +980,11 @@ func arrowToValue(ctx context.Context, rowIdx int, srcColumnMeta execResponseRow
 		if ok {
 			// structured map as json
 			if !srcValue.IsNull(rowIdx) {
-				return jsonToMap(ctx, srcColumnMeta, strings.Value(rowIdx), params)
+				return jsonToMap(ctx, srcColumnMeta.Fields[0], srcColumnMeta.Fields[1], strings.Value(rowIdx), params)
 			}
 		} else {
 			// structured map as native arrow
-			arrowMap := srcValue.(*array.Map)
-			if arrowMap.IsNull(rowIdx) {
-				return nil, nil
-			}
-			keys := arrowMap.Keys()
-			items := arrowMap.Items()
-			offsets := arrowMap.Offsets()
-			switch srcColumnMeta.Fields[0].Type {
-			case "text":
-				keyFunc := func(j int) (string, error) {
-					return keys.(*array.String).Value(j), nil
-				}
-				return buildStructuredMapFromArrow(ctx, rowIdx, srcColumnMeta, offsets, keyFunc, items, higherPrecision, loc)
-			case "fixed":
-				keyFunc := func(j int) (int64, error) {
-					k, err := extractInt64(keys, int(j))
-					if err != nil {
-						return 0, err
-					}
-					return k, nil
-				}
-				return buildStructuredMapFromArrow(ctx, rowIdx, srcColumnMeta, offsets, keyFunc, items, higherPrecision, loc)
-			}
+			return buildMapFromNativeArrow(ctx, rowIdx, srcColumnMeta.Fields[0], srcColumnMeta.Fields[1], srcValue, loc, higherPrecision, params)
 		}
 	case binaryType:
 		return arrowBinaryToValue(srcValue.(*array.Binary), rowIdx), nil
@@ -1022,6 +1003,153 @@ func arrowToValue(ctx context.Context, rowIdx int, srcColumnMeta execResponseRow
 	return nil, fmt.Errorf("unsupported data type")
 }
 
+func buildMapFromNativeArrow(ctx context.Context, rowIdx int, keyMetadata, valueMetadata fieldMetadata, srcValue arrow.Array, loc *time.Location, higherPrecision bool, params map[string]*string) (snowflakeValue, error) {
+	arrowMap := srcValue.(*array.Map)
+	if arrowMap.IsNull(rowIdx) {
+		return nil, nil
+	}
+	keys := arrowMap.Keys()
+	items := arrowMap.Items()
+	offsets := arrowMap.Offsets()
+	switch keyMetadata.Type {
+	case "text":
+		keyFunc := func(j int) (string, error) {
+			return keys.(*array.String).Value(j), nil
+		}
+		return buildStructuredMapFromArrow(ctx, rowIdx, valueMetadata, offsets, keyFunc, items, higherPrecision, loc, params)
+	case "fixed":
+		keyFunc := func(j int) (int64, error) {
+			k, err := extractInt64(keys, int(j))
+			if err != nil {
+				return 0, err
+			}
+			return k, nil
+		}
+		return buildStructuredMapFromArrow(ctx, rowIdx, valueMetadata, offsets, keyFunc, items, higherPrecision, loc, params)
+	}
+	return nil, nil
+}
+
+func buildListFromNativeArrow(ctx context.Context, rowIdx int, fieldMetadata fieldMetadata, srcValue arrow.Array, loc *time.Location, higherPrecision bool, params map[string]*string) (snowflakeValue, error) {
+	list := srcValue.(*array.List)
+	if list.IsNull(rowIdx) {
+		return nil, nil
+	}
+	values := list.ListValues()
+	offsets := list.Offsets()
+	snowflakeType := getSnowflakeType(fieldMetadata.Type)
+	switch snowflakeType {
+	case fixedType:
+		switch typedValues := values.(type) {
+		case *array.Decimal128:
+			if higherPrecision && fieldMetadata.Scale == 0 {
+				return mapStructuredArrayNativeArrowRows(offsets, rowIdx, func(j int) (*big.Int, error) {
+					return arrowDecimal128ToValue(typedValues, j, higherPrecision, fieldMetadata.Scale).(*big.Int), nil
+				})
+			} else if higherPrecision && fieldMetadata.Scale != 0 {
+				return mapStructuredArrayNativeArrowRows(offsets, rowIdx, func(j int) (*big.Float, error) {
+					return arrowDecimal128ToValue(typedValues, j, higherPrecision, fieldMetadata.Scale).(*big.Float), nil
+				})
+			} else if !higherPrecision && fieldMetadata.Scale == 0 {
+				return mapStructuredArrayNativeArrowRows(offsets, rowIdx, func(j int) (int64, error) {
+					v := arrowDecimal128ToValue(typedValues, j, higherPrecision, fieldMetadata.Scale)
+					return strconv.ParseInt(v.(string), 10, 64)
+				})
+			} else {
+				return mapStructuredArrayNativeArrowRows(offsets, rowIdx, func(j int) (float64, error) {
+					v := arrowDecimal128ToValue(typedValues, j, higherPrecision, fieldMetadata.Scale)
+					return strconv.ParseFloat(v.(string), 64)
+				})
+			}
+		case *array.Int64:
+			return mapStructuredArrayNativeArrowRows(offsets, rowIdx, func(j int) (int64, error) {
+				return arrowInt64ToValue(typedValues, j, higherPrecision, fieldMetadata.Scale).(int64), nil
+			})
+		case *array.Int32:
+			return mapStructuredArrayNativeArrowRows(offsets, rowIdx, func(j int) (int32, error) {
+				return arrowInt32ToValue(typedValues, j, higherPrecision, fieldMetadata.Scale).(int32), nil
+			})
+		case *array.Int16:
+			return mapStructuredArrayNativeArrowRows(offsets, rowIdx, func(j int) (int16, error) {
+				return arrowInt16ToValue(typedValues, j, higherPrecision, fieldMetadata.Scale).(int16), nil
+			})
+		case *array.Int8:
+			return mapStructuredArrayNativeArrowRows(offsets, rowIdx, func(j int) (int8, error) {
+				return arrowInt8ToValue(typedValues, j, higherPrecision, fieldMetadata.Scale).(int8), nil
+			})
+		}
+	case realType:
+		return mapStructuredArrayNativeArrowRows(offsets, rowIdx, func(j int) (float64, error) {
+			return arrowRealToValue(values.(*array.Float64), j).(float64), nil
+		})
+	case textType:
+		return mapStructuredArrayNativeArrowRows(offsets, rowIdx, func(j int) (string, error) {
+			return arrowStringToValue(values.(*array.String), j).(string), nil
+		})
+	case booleanType:
+		return mapStructuredArrayNativeArrowRows(offsets, rowIdx, func(j int) (bool, error) {
+			return arrowBoolToValue(values.(*array.Boolean), j).(bool), nil
+		})
+	case binaryType:
+		return mapStructuredArrayNativeArrowRows(offsets, rowIdx, func(j int) ([]byte, error) {
+			return arrowBinaryToValue(values.(*array.Binary), j).([]byte), nil
+		})
+	case dateType:
+		return mapStructuredArrayNativeArrowRows(offsets, rowIdx, func(j int) (time.Time, error) {
+			return arrowDateToValue(values.(*array.Date32), int(j)).(time.Time), nil
+		})
+	case timeType:
+		return mapStructuredArrayNativeArrowRows(offsets, rowIdx, func(j int) (time.Time, error) {
+			return arrowTimeToValue(values, int(j), fieldMetadata.Scale).(time.Time), nil
+		})
+	case timestampNtzType, timestampLtzType, timestampTzType:
+		return mapStructuredArrayNativeArrowRows(offsets, rowIdx, func(j int) (time.Time, error) {
+			ptr := arrowSnowflakeTimestampToTime(values, snowflakeType, fieldMetadata.Scale, j, loc)
+			if ptr != nil {
+				return *ptr, nil
+			}
+			return time.Time{}, nil
+		})
+	case objectType:
+		return mapStructuredArrayNativeArrowRows(offsets, rowIdx, func(j int) (*structuredType, error) {
+			m := make(map[string]any, len(fieldMetadata.Fields))
+			for fieldIdx, field := range fieldMetadata.Fields {
+				m[field.Name] = values.(*array.Struct).Field(fieldIdx).ValueStr(int(j))
+			}
+			return buildStructuredTypeRecursive(ctx, m, fieldMetadata.Fields, params)
+		})
+	case arrayType:
+		switch fieldMetadata.Fields[0].Type {
+		case "text":
+			return buildArrowListRecursive[string](ctx, rowIdx, fieldMetadata, offsets, values, loc, higherPrecision, params)
+		case "fixed":
+			if fieldMetadata.Fields[0].Scale == 0 {
+				return buildArrowListRecursive[int64](ctx, rowIdx, fieldMetadata, offsets, values, loc, higherPrecision, params)
+			}
+			return buildArrowListRecursive[float64](ctx, rowIdx, fieldMetadata, offsets, values, loc, higherPrecision, params)
+		case "real":
+			return buildArrowListRecursive[float64](ctx, rowIdx, fieldMetadata, offsets, values, loc, higherPrecision, params)
+		case "boolean":
+			return buildArrowListRecursive[bool](ctx, rowIdx, fieldMetadata, offsets, values, loc, higherPrecision, params)
+		case "binary":
+			return buildArrowListRecursive[[]byte](ctx, rowIdx, fieldMetadata, offsets, values, loc, higherPrecision, params)
+		case "date", "time", "timestamp_ltz", "timestamp_ntz", "timestamp_tz":
+			return buildArrowListRecursive[time.Time](ctx, rowIdx, fieldMetadata, offsets, values, loc, higherPrecision, params)
+		}
+	}
+	return nil, nil
+}
+
+func buildArrowListRecursive[T any](ctx context.Context, rowIdx int, fieldMetadata fieldMetadata, offsets []int32, values arrow.Array, loc *time.Location, higherPrecision bool, params map[string]*string) (snowflakeValue, error) {
+	return mapStructuredArrayNativeArrowRows(offsets, rowIdx, func(j int) ([]T, error) {
+		arrowList, err := buildListFromNativeArrow(ctx, j, fieldMetadata.Fields[0], values, loc, higherPrecision, params)
+		if err != nil {
+			return nil, err
+		}
+		return arrowList.([]T), nil
+	})
+}
+
 func mapStructuredArrayNativeArrowRows[T any](offsets []int32, rowIdx int, createValueFunc func(j int) (T, error)) (snowflakeValue, error) {
 	arr := make([]T, offsets[rowIdx+1]-offsets[rowIdx])
 	for j := offsets[rowIdx]; j < offsets[rowIdx+1]; j++ {
@@ -1050,9 +1178,9 @@ func extractInt64(values arrow.Array, j int) (int64, error) {
 	return 0, fmt.Errorf("unsupported map type: %T", values.DataType().Name())
 }
 
-func buildStructuredMapFromArrow[K comparable](ctx context.Context, rowIdx int, srcColumnMeta execResponseRowType, offsets []int32, keyFunc func(j int) (K, error), items arrow.Array, higherPrecision bool, loc *time.Location) (snowflakeValue, error) {
+func buildStructuredMapFromArrow[K comparable](ctx context.Context, rowIdx int, valueMetadata fieldMetadata, offsets []int32, keyFunc func(j int) (K, error), items arrow.Array, higherPrecision bool, loc *time.Location, params map[string]*string) (snowflakeValue, error) {
 	mapNullValuesEnabled := mapValuesNullableEnabled(ctx)
-	switch srcColumnMeta.Fields[1].Type {
+	switch valueMetadata.Type {
 	case "text":
 		if mapNullValuesEnabled {
 			return mapStructuredMapNativeArrowRows(make(map[K]sql.NullString), offsets, rowIdx, keyFunc, func(j int) (sql.NullString, error) {
@@ -1078,27 +1206,27 @@ func buildStructuredMapFromArrow[K comparable](ctx context.Context, rowIdx int,
 			return items.(*array.Boolean).Value(j), nil
 		})
 	case "fixed":
-		if higherPrecision && srcColumnMeta.Fields[1].Scale == 0 {
+		if higherPrecision && valueMetadata.Scale == 0 {
 			return mapStructuredMapNativeArrowRows(make(map[K]*big.Int), offsets, rowIdx, keyFunc, func(j int) (*big.Int, error) {
 				if items.IsNull(j) {
 					return nil, nil
 				}
-				return mapStructuredMapNativeArrowFixedValue[*big.Int](srcColumnMeta, j, items, higherPrecision, nil)
+				return mapStructuredMapNativeArrowFixedValue[*big.Int](valueMetadata, j, items, higherPrecision, nil)
 			})
-		} else if higherPrecision && srcColumnMeta.Fields[1].Scale != 0 {
+		} else if higherPrecision && valueMetadata.Scale != 0 {
 			return mapStructuredMapNativeArrowRows(make(map[K]*big.Float), offsets, rowIdx, keyFunc, func(j int) (*big.Float, error) {
 				if items.IsNull(j) {
 					return nil, nil
 				}
-				return mapStructuredMapNativeArrowFixedValue[*big.Float](srcColumnMeta, j, items, higherPrecision, nil)
+				return mapStructuredMapNativeArrowFixedValue[*big.Float](valueMetadata, j, items, higherPrecision, nil)
 			})
-		} else if !higherPrecision && srcColumnMeta.Fields[1].Scale == 0 {
+		} else if !higherPrecision && valueMetadata.Scale == 0 {
 			if mapNullValuesEnabled {
 				return mapStructuredMapNativeArrowRows(make(map[K]sql.NullInt64), offsets, rowIdx, keyFunc, func(j int) (sql.NullInt64, error) {
 					if items.IsNull(j) {
 						return sql.NullInt64{Valid: false}, nil
 					}
-					s, err := mapStructuredMapNativeArrowFixedValue[string](srcColumnMeta, j, items, higherPrecision, "")
+					s, err := mapStructuredMapNativeArrowFixedValue[string](valueMetadata, j, items, higherPrecision, "")
 					if err != nil {
 						return sql.NullInt64{}, err
 					}
@@ -1107,7 +1235,7 @@ func buildStructuredMapFromArrow[K comparable](ctx context.Context, rowIdx int,
 				})
 			}
 			return mapStructuredMapNativeArrowRows(make(map[K]int64), offsets, rowIdx, keyFunc, func(j int) (int64, error) {
-				s, err := mapStructuredMapNativeArrowFixedValue[string](srcColumnMeta, j, items, higherPrecision, "")
+				s, err := mapStructuredMapNativeArrowFixedValue[string](valueMetadata, j, items, higherPrecision, "")
 				if err != nil {
 					return 0, err
 				}
@@ -1119,7 +1247,7 @@ func buildStructuredMapFromArrow[K comparable](ctx context.Context, rowIdx int,
 					if items.IsNull(j) {
 						return sql.NullFloat64{Valid: false}, nil
 					}
-					s, err := mapStructuredMapNativeArrowFixedValue[string](srcColumnMeta, j, items, higherPrecision, "")
+					s, err := mapStructuredMapNativeArrowFixedValue[string](valueMetadata, j, items, higherPrecision, "")
 					if err != nil {
 						return sql.NullFloat64{}, err
 					}
@@ -1128,7 +1256,7 @@ func buildStructuredMapFromArrow[K comparable](ctx context.Context, rowIdx int,
 				})
 			}
 			return mapStructuredMapNativeArrowRows(make(map[K]float64), offsets, rowIdx, keyFunc, func(j int) (float64, error) {
-				s, err := mapStructuredMapNativeArrowFixedValue[string](srcColumnMeta, j, items, higherPrecision, "")
+				s, err := mapStructuredMapNativeArrowFixedValue[string](valueMetadata, j, items, higherPrecision, "")
 				if err != nil {
 					return 0, err
 				}
@@ -1161,14 +1289,62 @@ func buildStructuredMapFromArrow[K comparable](ctx context.Context, rowIdx int,
 		})
 	case "time":
 		return buildTimeFromNativeArrowArray(mapNullValuesEnabled, offsets, rowIdx, keyFunc, items, func(j int) time.Time {
-			return arrowTimeToValue(items, j, srcColumnMeta.Fields[1].Scale).(time.Time)
+			return arrowTimeToValue(items, j, valueMetadata.Scale).(time.Time)
 		})
 	case "timestamp_ltz", "timestamp_ntz", "timestamp_tz":
 		return buildTimeFromNativeArrowArray(mapNullValuesEnabled, offsets, rowIdx, keyFunc, items, func(j int) time.Time {
-			return *arrowSnowflakeTimestampToTime(items, getSnowflakeType(srcColumnMeta.Fields[1].Type), srcColumnMeta.Fields[1].Scale, j, loc)
+			return *arrowSnowflakeTimestampToTime(items, getSnowflakeType(valueMetadata.Type), valueMetadata.Scale, j, loc)
+		})
+	case "object":
+		return mapStructuredMapNativeArrowRows(make(map[K]*structuredType), offsets, rowIdx, keyFunc, func(j int) (*structuredType, error) {
+			if items.IsNull(j) {
+				return nil, nil
+			}
+			var err error
+			m := make(map[string]any)
+			for fieldIdx, field := range valueMetadata.Fields {
+				snowflakeType := getSnowflakeType(field.Type)
+				m[field.Name], err = arrowToValue(ctx, j, field, items.(*array.Struct).Field(fieldIdx), loc, higherPrecision, params, snowflakeType)
+				if err != nil {
+					return nil, err
+				}
+			}
+			return &structuredType{
+				values:        m,
+				fieldMetadata: valueMetadata.Fields,
+				params:        params,
+			}, nil
 		})
+	case "array":
+		switch valueMetadata.Fields[0].Type {
+		case "text":
+			return buildListFromNativeArrowMap[K, string](ctx, rowIdx, valueMetadata, offsets, keyFunc, items, higherPrecision, loc, params)
+		case "fixed":
+			if valueMetadata.Fields[0].Scale == 0 {
+				return buildListFromNativeArrowMap[K, int64](ctx, rowIdx, valueMetadata, offsets, keyFunc, items, higherPrecision, loc, params)
+			}
+			return buildListFromNativeArrowMap[K, float64](ctx, rowIdx, valueMetadata, offsets, keyFunc, items, higherPrecision, loc, params)
+		case "real":
+			return buildListFromNativeArrowMap[K, float64](ctx, rowIdx, valueMetadata, offsets, keyFunc, items, higherPrecision, loc, params)
+		case "binary":
+			return buildListFromNativeArrowMap[K, []byte](ctx, rowIdx, valueMetadata, offsets, keyFunc, items, higherPrecision, loc, params)
+		case "boolean":
+			return buildListFromNativeArrowMap[K, bool](ctx, rowIdx, valueMetadata, offsets, keyFunc, items, higherPrecision, loc, params)
+		case "date", "time", "timestamp_ltz", "timestamp_ntz", "timestamp_tz":
+			return buildListFromNativeArrowMap[K, time.Time](ctx, rowIdx, valueMetadata, offsets, keyFunc, items, higherPrecision, loc, params)
+		}
 	}
-	return nil, errors.New("Unsupported map value: " + srcColumnMeta.Fields[1].Type)
+	return nil, errors.New("Unsupported map value: " + valueMetadata.Type)
+}
+
+func buildListFromNativeArrowMap[K comparable, V any](ctx context.Context, rowIdx int, valueMetadata fieldMetadata, offsets []int32, keyFunc func(j int) (K, error), items arrow.Array, higherPrecision bool, loc *time.Location, params map[string]*string) (snowflakeValue, error) {
+	return mapStructuredMapNativeArrowRows(make(map[K][]V), offsets, rowIdx, keyFunc, func(j int) ([]V, error) {
+		if items.IsNull(j) {
+			return []V{}, nil
+		}
+		list, err := buildListFromNativeArrow(ctx, j, valueMetadata.Fields[0], items, loc, higherPrecision, params)
+		return list.([]V), err
+	})
 }
 
 func buildTimeFromNativeArrowArray[K comparable](mapNullValuesEnabled bool, offsets []int32, rowIdx int, keyFunc func(j int) (K, error), items arrow.Array, buildTime func(j int) time.Time) (snowflakeValue, error) {
@@ -1185,8 +1361,8 @@ func buildTimeFromNativeArrowArray[K comparable](mapNullValuesEnabled bool, offs
 	})
 }
 
-func mapStructuredMapNativeArrowFixedValue[V any](srcColumnMeta execResponseRowType, j int, items arrow.Array, higherPrecision bool, defaultValue V) (V, error) {
-	v, err := extractNumberFromArrow(&items, j, higherPrecision, srcColumnMeta.Fields[1].Scale)
+func mapStructuredMapNativeArrowFixedValue[V any](valueMetadata fieldMetadata, j int, items arrow.Array, higherPrecision bool, defaultValue V) (V, error) {
+	v, err := extractNumberFromArrow(&items, j, higherPrecision, valueMetadata.Scale)
 	if err != nil {
 		return defaultValue, err
 	}
@@ -1222,7 +1398,8 @@ func mapStructuredMapNativeArrowRows[K comparable, V any](m map[K]V, offsets []i
 	return m, nil
 }
 
-func arrowToStructuredType(structs *array.Struct, fieldMetadata []fieldMetadata, loc *time.Location, rowIdx int, higherPrecision bool, params map[string]*string) *structuredType {
+func arrowToStructuredType(ctx context.Context, structs *array.Struct, fieldMetadata []fieldMetadata, loc *time.Location, rowIdx int, higherPrecision bool, params map[string]*string) (*structuredType, error) {
+	var err error
 	m := make(map[string]any)
 	for colIdx := 0; colIdx < structs.NumField(); colIdx++ {
 		var v any
@@ -1260,7 +1437,23 @@ func arrowToStructuredType(structs *array.Struct, fieldMetadata []fieldMetadata,
 			}
 		case objectType:
 			if !structs.Field(colIdx).IsNull(rowIdx) {
-				v = arrowToStructuredType(structs.Field(colIdx).(*array.Struct), fieldMetadata[colIdx].Fields, loc, rowIdx, higherPrecision, params)
+				if v, err = arrowToStructuredType(ctx, structs.Field(colIdx).(*array.Struct), fieldMetadata[colIdx].Fields, loc, rowIdx, higherPrecision, params); err != nil {
+					return nil, err
+				}
+			}
+		case arrayType:
+			if !structs.Field(colIdx).IsNull(rowIdx) {
+				var err error
+				if v, err = buildListFromNativeArrow(ctx, rowIdx, fieldMetadata[colIdx].Fields[0], structs.Field(colIdx), loc, higherPrecision, params); err != nil {
+					return nil, err
+				}
+			}
+		case mapType:
+			if !structs.Field(colIdx).IsNull(rowIdx) {
+				var err error
+				if v, err = buildMapFromNativeArrow(ctx, rowIdx, fieldMetadata[colIdx].Fields[0], fieldMetadata[colIdx].Fields[1], structs.Field(colIdx), loc, higherPrecision, params); err != nil {
+					return nil, err
+				}
 			}
 		}
 		m[fieldMetadata[colIdx].Name] = v
@@ -1269,7 +1462,7 @@ func arrowToStructuredType(structs *array.Struct, fieldMetadata []fieldMetadata,
 		values:        m,
 		fieldMetadata: fieldMetadata,
 		params:        params,
-	}
+	}, nil
 }
 
 func arrowStringToValue(srcValue *array.String, rowIdx int) snowflakeValue {
@@ -2005,3 +2198,9 @@ func convertTzTypeToSnowflakeType(tzType timezoneType) snowflakeType {
 	}
 	return unSupportedType
 }
+
+func decoderWithNumbersAsStrings(srcValue *string) *json.Decoder {
+	decoder := json.NewDecoder(bytes.NewBufferString(*srcValue))
+	decoder.UseNumber()
+	return decoder
+}
diff --git a/query.go b/query.go
index d81f5c0..a861709 100644
--- a/query.go
+++ b/query.go
@@ -57,6 +57,15 @@ type execResponseRowType struct {
 	Nullable   bool            `json:"nullable"`
 }
 
+func (ex *execResponseRowType) toFieldMetadata() fieldMetadata {
+	return fieldMetadata{
+		ex.Name,
+		ex.Type,
+		int(ex.Scale),
+		ex.Fields,
+	}
+}
+
 type fieldMetadata struct {
 	Name   string          `json:"name"`
 	Type   string          `json:"type"`
diff --git a/structured_type.go b/structured_type.go
index d59a3f8..3e5750e 100644
--- a/structured_type.go
+++ b/structured_type.go
@@ -40,6 +40,7 @@ type StructuredObject interface {
 	GetTime(fieldName string) (time.Time, error)
 	GetNullTime(fieldName string) (sql.NullTime, error)
 	GetStruct(fieldName string, scanner sql.Scanner) (sql.Scanner, error)
+	GetRaw(fieldName string) (any, error)
 }
 
 // ArrayOfScanners Helper type for scanning array of sql.Scanner values.
@@ -60,12 +61,38 @@ func (st *ArrayOfScanners[T]) Scan(val any) error {
 
 // ScanArrayOfScanners is a helper function for scanning arrays of sql.Scanner values.
 // Example:
-// var res []*simpleObject
-// err := rows.Scan(ScanArrayOfScanners(&res))
+//
+//	var res []*simpleObject
+//	err := rows.Scan(ScanArrayOfScanners(&res))
 func ScanArrayOfScanners[T sql.Scanner](value *[]T) *ArrayOfScanners[T] {
 	return (*ArrayOfScanners[T])(value)
 }
 
+// MapOfScanners Helper type for scanning map of sql.Scanner values.
+type MapOfScanners[K comparable, V sql.Scanner] map[K]V
+
+func (st *MapOfScanners[K, V]) Scan(val any) error {
+	sts := val.(map[K]*structuredType)
+	*st = make(map[K]V)
+	var someV V
+	for k := range sts {
+		(*st)[k] = reflect.New(reflect.TypeOf(someV).Elem()).Interface().(V)
+		if err := (*st)[k].Scan(sts[k]); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+// ScanMapOfScanners is a helper function for scanning maps of sql.Scanner values.
+// Example:
+//
+//	var res map[string]*simpleObject
+//	err := rows.Scan(ScanMapOfScanners(&res))
+func ScanMapOfScanners[K comparable, V sql.Scanner](m *map[K]V) *MapOfScanners[K, V] {
+	return (*MapOfScanners[K, V])(m)
+}
+
 type structuredType struct {
 	values        map[string]any
 	fieldMetadata []fieldMetadata
@@ -357,6 +384,9 @@ func (st *structuredType) GetStruct(fieldName string, scanner sql.Scanner) (sql.
 	err = scanner.Scan(childSt)
 	return scanner, err
 }
+func (st *structuredType) GetRaw(fieldName string) (any, error) {
+	return st.values[fieldName], nil
+}
 
 func (st *structuredType) fieldMetadataByFieldName(fieldName string) (fieldMetadata, error) {
 	for _, fm := range st.fieldMetadata {


Test patch:
diff --git a/structured_type_test.go b/structured_type_test.go
index 31954bc..4ac7891 100644
--- a/structured_type_test.go
+++ b/structured_type_test.go
@@ -27,6 +27,9 @@ type objectWithAllTypes struct {
 	tz        time.Time
 	ntz       time.Time
 	so        simpleObject
+	sArr      []string
+	f64Arr    []float64
+	someMap   map[string]bool
 }
 
 func (o *objectWithAllTypes) Scan(val any) error {
@@ -82,6 +85,21 @@ func (o *objectWithAllTypes) Scan(val any) error {
 		return err
 	}
 	o.so = *so.(*simpleObject)
+	sArr, err := st.GetRaw("sArr")
+	if err != nil {
+		return err
+	}
+	o.sArr = sArr.([]string)
+	f64Arr, err := st.GetRaw("f64Arr")
+	if err != nil {
+		return err
+	}
+	o.f64Arr = f64Arr.([]float64)
+	someMap, err := st.GetRaw("someMap")
+	if err != nil {
+		return err
+	}
+	o.someMap = someMap.(map[string]bool)
 	return nil
 }
 
@@ -115,7 +133,7 @@ func TestObjectWithAllTypes(t *testing.T) {
 	runDBTest(t, func(dbt *DBTest) {
 		dbt.mustExec("ALTER SESSION SET TIMEZONE = 'Europe/Warsaw'")
 		forAllStructureTypeFormats(dbt, func(t *testing.T, format string) {
-			rows := dbt.mustQuery("SELECT 1, {'s': 'some string', 'b': 1, 'i16': 2, 'i32': 3, 'i64': 9223372036854775807, 'f32': '1.1', 'f64': 2.2, 'nfraction': 3.3, 'bo': true, 'bi': TO_BINARY('616263', 'HEX'), 'date': '2024-03-21'::DATE, 'time': '13:03:02'::TIME, 'ltz': '2021-07-21 11:22:33'::TIMESTAMP_LTZ, 'tz': '2022-08-31 13:43:22 +0200'::TIMESTAMP_TZ, 'ntz': '2023-05-22 01:17:19'::TIMESTAMP_NTZ, 'so': {'s': 'child', 'i': 9}}::OBJECT(s VARCHAR, b TINYINT, i16 SMALLINT, i32 INTEGER, i64 BIGINT, f32 FLOAT, f64 DOUBLE, nfraction NUMBER(38, 19), bo BOOLEAN, bi BINARY, date DATE, time TIME, ltz TIMESTAMP_LTZ, tz TIMESTAMP_TZ, ntz TIMESTAMP_NTZ, so OBJECT(s VARCHAR, i INTEGER))")
+			rows := dbt.mustQuery("SELECT 1, {'s': 'some string', 'b': 1, 'i16': 2, 'i32': 3, 'i64': 9223372036854775807, 'f32': '1.1', 'f64': 2.2, 'nfraction': 3.3, 'bo': true, 'bi': TO_BINARY('616263', 'HEX'), 'date': '2024-03-21'::DATE, 'time': '13:03:02'::TIME, 'ltz': '2021-07-21 11:22:33'::TIMESTAMP_LTZ, 'tz': '2022-08-31 13:43:22 +0200'::TIMESTAMP_TZ, 'ntz': '2023-05-22 01:17:19'::TIMESTAMP_NTZ, 'so': {'s': 'child', 'i': 9}, 'sArr': ARRAY_CONSTRUCT('x', 'y', 'z'), 'f64Arr': ARRAY_CONSTRUCT(1.1, 2.2, 3.3), 'someMap': {'x': true, 'y': false}}::OBJECT(s VARCHAR, b TINYINT, i16 SMALLINT, i32 INTEGER, i64 BIGINT, f32 FLOAT, f64 DOUBLE, nfraction NUMBER(38, 19), bo BOOLEAN, bi BINARY, date DATE, time TIME, ltz TIMESTAMP_LTZ, tz TIMESTAMP_TZ, ntz TIMESTAMP_NTZ, so OBJECT(s VARCHAR, i INTEGER), sArr ARRAY(VARCHAR), f64Arr ARRAY(DOUBLE), someMap MAP(VARCHAR, BOOLEAN))")
 			defer rows.Close()
 			rows.Next()
 			var ignore int
@@ -140,25 +158,31 @@ func TestObjectWithAllTypes(t *testing.T) {
 			assertTrueE(t, res.tz.Equal(time.Date(2022, time.August, 31, 13, 43, 22, 0, warsawTz)))
 			assertTrueE(t, res.ntz.Equal(time.Date(2023, time.May, 22, 1, 17, 19, 0, time.UTC)))
 			assertEqualE(t, res.so, simpleObject{s: "child", i: 9})
+			assertDeepEqualE(t, res.sArr, []string{"x", "y", "z"})
+			assertDeepEqualE(t, res.f64Arr, []float64{1.1, 2.2, 3.3})
+			assertDeepEqualE(t, res.someMap, map[string]bool{"x": true, "y": false})
 		})
 	})
 }
 
 type objectWithAllTypesNullable struct {
-	s    sql.NullString
-	b    sql.NullByte
-	i16  sql.NullInt16
-	i32  sql.NullInt32
-	i64  sql.NullInt64
-	f64  sql.NullFloat64
-	bo   sql.NullBool
-	bi   []byte
-	date sql.NullTime
-	time sql.NullTime
-	ltz  sql.NullTime
-	tz   sql.NullTime
-	ntz  sql.NullTime
-	so   *simpleObject
+	s       sql.NullString
+	b       sql.NullByte
+	i16     sql.NullInt16
+	i32     sql.NullInt32
+	i64     sql.NullInt64
+	f64     sql.NullFloat64
+	bo      sql.NullBool
+	bi      []byte
+	date    sql.NullTime
+	time    sql.NullTime
+	ltz     sql.NullTime
+	tz      sql.NullTime
+	ntz     sql.NullTime
+	so      *simpleObject
+	sArr    []string
+	f64Arr  []float64
+	someMap map[string]bool
 }
 
 func (o *objectWithAllTypesNullable) Scan(val any) error {
@@ -212,6 +236,27 @@ func (o *objectWithAllTypesNullable) Scan(val any) error {
 	} else {
 		o.so = nil
 	}
+	sArr, err := st.GetRaw("sArr")
+	if err != nil {
+		return err
+	}
+	if sArr != nil {
+		o.sArr = sArr.([]string)
+	}
+	f64Arr, err := st.GetRaw("f64Arr")
+	if err != nil {
+		return err
+	}
+	if f64Arr != nil {
+		o.f64Arr = f64Arr.([]float64)
+	}
+	someMap, err := st.GetRaw("someMap")
+	if err != nil {
+		return err
+	}
+	if someMap != nil {
+		o.someMap = someMap.(map[string]bool)
+	}
 	return nil
 }
 
@@ -223,7 +268,7 @@ func TestObjectWithAllTypesNullable(t *testing.T) {
 		dbt.mustExec("ALTER SESSION SET TIMEZONE = 'Europe/Warsaw'")
 		forAllStructureTypeFormats(dbt, func(t *testing.T, format string) {
 			t.Run("null", func(t *testing.T) {
-				rows := dbt.mustQuery("select null, object_construct_keep_null('s', null, 'b', null, 'i16', null, 'i32', null, 'i64', null, 'f64', null, 'bo', null, 'bi', null, 'date', null, 'time', null, 'ltz', null, 'tz', null, 'ntz', null, 'so', null)::OBJECT(s VARCHAR, b TINYINT, i16 SMALLINT, i32 INTEGER, i64 BIGINT, f64 DOUBLE, bo BOOLEAN, bi BINARY, date DATE, time TIME, ltz TIMESTAMP_LTZ, tz TIMESTAMP_TZ, ntz TIMESTAMP_NTZ, so OBJECT(s VARCHAR, i INTEGER))")
+				rows := dbt.mustQuery("select null, object_construct_keep_null('s', null, 'b', null, 'i16', null, 'i32', null, 'i64', null, 'f64', null, 'bo', null, 'bi', null, 'date', null, 'time', null, 'ltz', null, 'tz', null, 'ntz', null, 'so', null, 'sArr', null, 'f64Arr', null, 'someMap', null)::OBJECT(s VARCHAR, b TINYINT, i16 SMALLINT, i32 INTEGER, i64 BIGINT, f64 DOUBLE, bo BOOLEAN, bi BINARY, date DATE, time TIME, ltz TIMESTAMP_LTZ, tz TIMESTAMP_TZ, ntz TIMESTAMP_NTZ, so OBJECT(s VARCHAR, i INTEGER), sArr ARRAY(VARCHAR), f64Arr ARRAY(DOUBLE), someMap MAP(VARCHAR, BOOLEAN))")
 				defer rows.Close()
 				rows.Next()
 				var ignore sql.NullInt32
@@ -248,7 +293,7 @@ func TestObjectWithAllTypesNullable(t *testing.T) {
 				assertDeepEqualE(t, res.so, so)
 			})
 			t.Run("not null", func(t *testing.T) {
-				rows := dbt.mustQuery("select 1, object_construct_keep_null('s', 'abc', 'b', 1, 'i16', 2, 'i32', 3, 'i64', 9223372036854775807, 'f64', 2.2, 'bo', true, 'bi', TO_BINARY('616263', 'HEX'), 'date', '2024-03-21'::DATE, 'time', '13:03:02'::TIME, 'ltz', '2021-07-21 11:22:33'::TIMESTAMP_LTZ, 'tz', '2022-08-31 13:43:22 +0200'::TIMESTAMP_TZ, 'ntz', '2023-05-22 01:17:19'::TIMESTAMP_NTZ, 'so', {'s': 'child', 'i': 9}::OBJECT)::OBJECT(s VARCHAR, b TINYINT, i16 SMALLINT, i32 INTEGER, i64 BIGINT, f64 DOUBLE, bo BOOLEAN, bi BINARY, date DATE, time TIME, ltz TIMESTAMP_LTZ, tz TIMESTAMP_TZ, ntz TIMESTAMP_NTZ, so OBJECT(s VARCHAR, i INTEGER))")
+				rows := dbt.mustQuery("select 1, object_construct_keep_null('s', 'abc', 'b', 1, 'i16', 2, 'i32', 3, 'i64', 9223372036854775807, 'f64', 2.2, 'bo', true, 'bi', TO_BINARY('616263', 'HEX'), 'date', '2024-03-21'::DATE, 'time', '13:03:02'::TIME, 'ltz', '2021-07-21 11:22:33'::TIMESTAMP_LTZ, 'tz', '2022-08-31 13:43:22 +0200'::TIMESTAMP_TZ, 'ntz', '2023-05-22 01:17:19'::TIMESTAMP_NTZ, 'so', {'s': 'child', 'i': 9}::OBJECT, 'sArr', ARRAY_CONSTRUCT('x', 'y', 'z'), 'f64Arr', ARRAY_CONSTRUCT(1.1, 2.2, 3.3), 'someMap', {'x': true, 'y': false})::OBJECT(s VARCHAR, b TINYINT, i16 SMALLINT, i32 INTEGER, i64 BIGINT, f64 DOUBLE, bo BOOLEAN, bi BINARY, date DATE, time TIME, ltz TIMESTAMP_LTZ, tz TIMESTAMP_TZ, ntz TIMESTAMP_NTZ, so OBJECT(s VARCHAR, i INTEGER), sArr ARRAY(VARCHAR), f64Arr ARRAY(DOUBLE), someMap MAP(VARCHAR, BOOLEAN))")
 				defer rows.Close()
 				rows.Next()
 				var ignore sql.NullInt32
@@ -276,28 +321,14 @@ func TestObjectWithAllTypesNullable(t *testing.T) {
 				assertTrueE(t, res.ntz.Valid)
 				assertTrueE(t, res.ntz.Time.Equal(time.Date(2023, time.May, 22, 1, 17, 19, 0, time.UTC)))
 				assertDeepEqualE(t, res.so, &simpleObject{s: "child", i: 9})
+				assertDeepEqualE(t, res.sArr, []string{"x", "y", "z"})
+				assertDeepEqualE(t, res.f64Arr, []float64{1.1, 2.2, 3.3})
+				assertDeepEqualE(t, res.someMap, map[string]bool{"x": true, "y": false})
 			})
 		})
 	})
 }
 
-type HigherPrecisionStruct struct {
-	i *big.Int
-	f *big.Float
-}
-
-func (hps *HigherPrecisionStruct) Scan(val any) error {
-	st := val.(StructuredObject)
-	var err error
-	if hps.i, err = st.GetBigInt("i"); err != nil {
-		return err
-	}
-	if hps.f, err = st.GetBigFloat("f"); err != nil {
-		return err
-	}
-	return nil
-}
-
 func TestObjectMetadata(t *testing.T) {
 	skipStructuredTypesTestsOnGHActions(t)
 	runDBTest(t, func(dbt *DBTest) {
@@ -569,6 +600,114 @@ func TestArrayOfObjects(t *testing.T) {
 	})
 }
 
+func TestArrayOfArrays(t *testing.T) {
+	warsawTz, err := time.LoadLocation("Europe/Warsaw")
+	assertNilF(t, err)
+	testcases := []struct {
+		name     string
+		query    string
+		actual   any
+		expected any
+	}{
+		{
+			name:     "string",
+			query:    "SELECT ARRAY_CONSTRUCT(ARRAY_CONSTRUCT('a', 'b', 'c'), ARRAY_CONSTRUCT('d', 'e'))::ARRAY(ARRAY(VARCHAR))",
+			actual:   make([][]string, 2),
+			expected: [][]string{{"a", "b", "c"}, {"d", "e"}},
+		},
+		{
+			name:     "int64",
+			query:    "SELECT ARRAY_CONSTRUCT(ARRAY_CONSTRUCT(1, 2), ARRAY_CONSTRUCT(3, 4))::ARRAY(ARRAY(INTEGER))",
+			actual:   make([][]int64, 2),
+			expected: [][]int64{{1, 2}, {3, 4}},
+		},
+		{
+			name:     "float64 - fixed",
+			query:    "SELECT ARRAY_CONSTRUCT(ARRAY_CONSTRUCT(1.1, 2.2), ARRAY_CONSTRUCT(3.3, 4.4))::ARRAY(ARRAY(NUMBER(38, 19)))",
+			actual:   make([][]float64, 2),
+			expected: [][]float64{{1.1, 2.2}, {3.3, 4.4}},
+		},
+		{
+			name:     "float64 - real",
+			query:    "SELECT ARRAY_CONSTRUCT(ARRAY_CONSTRUCT(1.1, 2.2), ARRAY_CONSTRUCT(3.3, 4.4))::ARRAY(ARRAY(DOUBLE))",
+			actual:   make([][]float64, 2),
+			expected: [][]float64{{1.1, 2.2}, {3.3, 4.4}},
+		},
+		{
+			name:     "bool",
+			query:    "SELECT ARRAY_CONSTRUCT(ARRAY_CONSTRUCT(true, false), ARRAY_CONSTRUCT(false, true, false))::ARRAY(ARRAY(BOOLEAN))",
+			actual:   make([][]bool, 2),
+			expected: [][]bool{{true, false}, {false, true, false}},
+		},
+		{
+			name:     "binary",
+			query:    "SELECT ARRAY_CONSTRUCT(ARRAY_CONSTRUCT(TO_BINARY('6162'), TO_BINARY('6364')), ARRAY_CONSTRUCT(TO_BINARY('6566'), TO_BINARY('6768')))::ARRAY(ARRAY(BINARY))",
+			actual:   make([][][]byte, 2),
+			expected: [][][]byte{{{'a', 'b'}, {'c', 'd'}}, {{'e', 'f'}, {'g', 'h'}}},
+		},
+		{
+			name:     "date",
+			query:    "SELECT ARRAY_CONSTRUCT(ARRAY_CONSTRUCT('2024-01-01'::DATE, '2024-02-02'::DATE), ARRAY_CONSTRUCT('2024-03-03'::DATE, '2024-04-04'::DATE))::ARRAY(ARRAY(DATE))",
+			actual:   make([][]time.Time, 2),
+			expected: [][]time.Time{{time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC), time.Date(2024, 2, 2, 0, 0, 0, 0, time.UTC)}, {time.Date(2024, 3, 3, 0, 0, 0, 0, time.UTC), time.Date(2024, 4, 4, 0, 0, 0, 0, time.UTC)}},
+		},
+		{
+			name:     "time",
+			query:    "SELECT ARRAY_CONSTRUCT(ARRAY_CONSTRUCT('01:01:01'::TIME, '02:02:02'::TIME), ARRAY_CONSTRUCT('03:03:03'::TIME, '04:04:04'::TIME))::ARRAY(ARRAY(TIME))",
+			actual:   make([][]time.Time, 2),
+			expected: [][]time.Time{{time.Date(0, 1, 1, 1, 1, 1, 0, time.UTC), time.Date(0, 1, 1, 2, 2, 2, 0, time.UTC)}, {time.Date(0, 1, 1, 3, 3, 3, 0, time.UTC), time.Date(0, 1, 1, 4, 4, 4, 0, time.UTC)}},
+		},
+		{
+			name:     "timestamp_ltz",
+			query:    "SELECT ARRAY_CONSTRUCT(ARRAY_CONSTRUCT('2024-01-05 11:22:33'::TIMESTAMP_LTZ), ARRAY_CONSTRUCT('2001-11-12 11:22:33'::TIMESTAMP_LTZ))::ARRAY(ARRAY(TIMESTAMP_LTZ))",
+			actual:   make([][]time.Time, 2),
+			expected: [][]time.Time{{time.Date(2024, time.January, 5, 11, 22, 33, 0, warsawTz)}, {time.Date(2001, time.November, 12, 11, 22, 33, 0, warsawTz)}},
+		},
+		{
+			name:     "timestamp_ntz",
+			query:    "SELECT ARRAY_CONSTRUCT(ARRAY_CONSTRUCT('2024-01-05 11:22:33'::TIMESTAMP_NTZ), ARRAY_CONSTRUCT('2001-11-12 11:22:33'::TIMESTAMP_NTZ))::ARRAY(ARRAY(TIMESTAMP_NTZ))",
+			actual:   make([][]time.Time, 2),
+			expected: [][]time.Time{{time.Date(2024, time.January, 5, 11, 22, 33, 0, time.UTC)}, {time.Date(2001, time.November, 12, 11, 22, 33, 0, time.UTC)}},
+		},
+		{
+			name:     "timestamp_tz",
+			query:    "SELECT ARRAY_CONSTRUCT(ARRAY_CONSTRUCT('2024-01-05 11:22:33 +0100'::TIMESTAMP_TZ), ARRAY_CONSTRUCT('2001-11-12 11:22:33 +0100'::TIMESTAMP_TZ))::ARRAY(ARRAY(TIMESTAMP_TZ))",
+			actual:   make([][]time.Time, 2),
+			expected: [][]time.Time{{time.Date(2024, time.January, 5, 11, 22, 33, 0, warsawTz)}, {time.Date(2001, time.November, 12, 11, 22, 33, 0, warsawTz)}},
+		},
+	}
+	skipStructuredTypesTestsOnGHActions(t)
+	runDBTest(t, func(dbt *DBTest) {
+		dbt.mustExec("ALTER SESSION SET TIMEZONE = 'Europe/Warsaw'")
+		forAllStructureTypeFormats(dbt, func(t *testing.T, format string) {
+			for _, tc := range testcases {
+				t.Run(tc.name, func(t *testing.T) {
+					rows := dbt.mustQuery(tc.query)
+					defer rows.Close()
+					rows.Next()
+					err := rows.Scan(&tc.actual)
+					assertNilF(t, err)
+					if timesOfTimes, ok := tc.expected.([][]time.Time); ok {
+						for i, timeOfTimes := range timesOfTimes {
+							for j, tm := range timeOfTimes {
+								if tc.name == "time" {
+									assertEqualE(t, tm.Hour(), tc.actual.([][]time.Time)[i][j].Hour())
+									assertEqualE(t, tm.Minute(), tc.actual.([][]time.Time)[i][j].Minute())
+									assertEqualE(t, tm.Second(), tc.actual.([][]time.Time)[i][j].Second())
+								} else {
+									assertTrueE(t, tm.Equal(tc.actual.([][]time.Time)[i][j]))
+								}
+							}
+						}
+					} else {
+						assertDeepEqualE(t, tc.actual, tc.expected)
+					}
+				})
+			}
+		})
+	})
+}
+
 func TestMapAndMetadata(t *testing.T) {
 	skipStructuredTypesTestsOnGHActions(t)
 	warsawTz, err := time.LoadLocation("Europe/Warsaw")
@@ -801,6 +940,275 @@ func TestMapAndMetadata(t *testing.T) {
 	})
 }
 
+func TestMapOfObjects(t *testing.T) {
+	skipStructuredTypesTestsOnGHActions(t)
+	runDBTest(t, func(dbt *DBTest) {
+		forAllStructureTypeFormats(dbt, func(t *testing.T, format string) {
+			rows := dbt.mustQuery("SELECT {'x': {'s': 'abc', 'i': 1}, 'y': {'s': 'def', 'i': 2}}::MAP(VARCHAR, OBJECT(s VARCHAR, i INTEGER))")
+			defer rows.Close()
+			var res map[string]*simpleObject
+			rows.Next()
+			err := rows.Scan(ScanMapOfScanners(&res))
+			assertNilF(t, err)
+			assertDeepEqualE(t, res, map[string]*simpleObject{"x": {s: "abc", i: 1}, "y": {s: "def", i: 2}})
+		})
+	})
+}
+
+func TestMapOfArrays(t *testing.T) {
+	skipStructuredTypesTestsOnGHActions(t)
+	warsawTz, err := time.LoadLocation("Europe/Warsaw")
+	assertNilF(t, err)
+	testcases := []struct {
+		name     string
+		query    string
+		actual   any
+		expected any
+	}{
+		{
+			name:     "string",
+			query:    "SELECT {'x': ARRAY_CONSTRUCT('ab', 'cd'), 'y': ARRAY_CONSTRUCT('ef')}::MAP(VARCHAR, ARRAY(VARCHAR))",
+			actual:   make(map[string][]string),
+			expected: map[string][]string{"x": {"ab", "cd"}, "y": {"ef"}},
+		},
+		{
+			name:     "fixed - scale == 0",
+			query:    "SELECT {'x': ARRAY_CONSTRUCT(1, 2), 'y': ARRAY_CONSTRUCT(3, 4)}::MAP(VARCHAR, ARRAY(INTEGER))",
+			actual:   make(map[string][]int64),
+			expected: map[string][]int64{"x": {1, 2}, "y": {3, 4}},
+		},
+		{
+			name:     "fixed - scale != 0",
+			query:    "SELECT {'x': ARRAY_CONSTRUCT(1.1, 2.2), 'y': ARRAY_CONSTRUCT(3.3, 4.4)}::MAP(VARCHAR, ARRAY(NUMBER(38, 19)))",
+			actual:   make(map[string][]float64),
+			expected: map[string][]float64{"x": {1.1, 2.2}, "y": {3.3, 4.4}},
+		},
+		{
+			name:     "real",
+			query:    "SELECT {'x': ARRAY_CONSTRUCT(1.1, 2.2), 'y': ARRAY_CONSTRUCT(3.3, 4.4)}::MAP(VARCHAR, ARRAY(DOUBLE))",
+			actual:   make(map[string][]float64),
+			expected: map[string][]float64{"x": {1.1, 2.2}, "y": {3.3, 4.4}},
+		},
+		{
+			name:     "binary",
+			query:    "SELECT {'x': ARRAY_CONSTRUCT(TO_BINARY('6162')), 'y': ARRAY_CONSTRUCT(TO_BINARY('6364'), TO_BINARY('6566'))}::MAP(VARCHAR, ARRAY(BINARY))",
+			actual:   make(map[string][][]byte),
+			expected: map[string][][]byte{"x": {[]byte{'a', 'b'}}, "y": {[]byte{'c', 'd'}, []byte{'e', 'f'}}},
+		},
+		{
+			name:     "boolean",
+			query:    "SELECT {'x': ARRAY_CONSTRUCT(true, false), 'y': ARRAY_CONSTRUCT(false, true)}::MAP(VARCHAR, ARRAY(BOOLEAN))",
+			actual:   make(map[string][]bool),
+			expected: map[string][]bool{"x": {true, false}, "y": {false, true}},
+		},
+		{
+			name:     "date",
+			query:    "SELECT {'a': ARRAY_CONSTRUCT('2024-04-02'::DATE, '2024-04-03'::DATE)}::MAP(VARCHAR, ARRAY(DATE))",
+			expected: map[string][]time.Time{"a": {time.Date(2024, time.April, 2, 0, 0, 0, 0, time.UTC), time.Date(2024, time.April, 3, 0, 0, 0, 0, time.UTC)}},
+			actual:   make(map[string]time.Time),
+		},
+		{
+			name:     "time",
+			query:    "SELECT {'a': ARRAY_CONSTRUCT('13:03:02'::TIME, '13:03:03'::TIME)}::MAP(VARCHAR, ARRAY(TIME))",
+			expected: map[string][]time.Time{"a": {time.Date(0, 0, 0, 13, 3, 2, 0, time.UTC), time.Date(0, 0, 0, 13, 3, 3, 0, time.UTC)}},
+			actual:   make(map[string]time.Time),
+		},
+		{
+			name:     "timestamp_ntz",
+			query:    "SELECT {'a': ARRAY_CONSTRUCT('2024-01-05 11:22:33'::TIMESTAMP_NTZ, '2024-01-06 11:22:33'::TIMESTAMP_NTZ)}::MAP(VARCHAR, ARRAY(TIMESTAMP_NTZ))",
+			expected: map[string][]time.Time{"a": {time.Date(2024, time.January, 5, 11, 22, 33, 0, time.UTC), time.Date(2024, time.January, 6, 11, 22, 33, 0, time.UTC)}},
+			actual:   make(map[string]time.Time),
+		},
+		{
+			name:     "string timestamp_tz",
+			query:    "SELECT {'a': ARRAY_CONSTRUCT('2024-01-05 11:22:33 +0100'::TIMESTAMP_TZ, '2024-01-06 11:22:33 +0100'::TIMESTAMP_TZ)}::MAP(VARCHAR, ARRAY(TIMESTAMP_TZ))",
+			expected: map[string][]time.Time{"a": {time.Date(2024, time.January, 5, 11, 22, 33, 0, warsawTz), time.Date(2024, time.January, 6, 11, 22, 33, 0, warsawTz)}},
+			actual:   make(map[string]time.Time),
+		},
+		{
+			name:     "string timestamp_ltz",
+			query:    "SELECT {'a': ARRAY_CONSTRUCT('2024-01-05 11:22:33'::TIMESTAMP_LTZ, '2024-01-06 11:22:33'::TIMESTAMP_LTZ)}::MAP(VARCHAR, ARRAY(TIMESTAMP_LTZ))",
+			expected: map[string][]time.Time{"a": {time.Date(2024, time.January, 5, 11, 22, 33, 0, warsawTz), time.Date(2024, time.January, 6, 11, 22, 33, 0, warsawTz)}},
+			actual:   make(map[string]time.Time),
+		},
+	}
+	runDBTest(t, func(dbt *DBTest) {
+		dbt.mustExec("ALTER SESSION SET TIMEZONE = 'Europe/Warsaw'")
+		forAllStructureTypeFormats(dbt, func(t *testing.T, format string) {
+			for _, tc := range testcases {
+				t.Run(tc.name, func(t *testing.T) {
+					rows := dbt.mustQuery(tc.query)
+					defer rows.Close()
+					rows.Next()
+					err := rows.Scan(&tc.actual)
+					assertNilF(t, err)
+					if expected, ok := tc.expected.(map[string][]time.Time); ok {
+						for k, v := range expected {
+							for i, expectedTime := range v {
+								if tc.name == "time" {
+									assertEqualE(t, expectedTime.Hour(), tc.actual.(map[string][]time.Time)[k][i].Hour())
+									assertEqualE(t, expectedTime.Minute(), tc.actual.(map[string][]time.Time)[k][i].Minute())
+									assertEqualE(t, expectedTime.Second(), tc.actual.(map[string][]time.Time)[k][i].Second())
+								} else {
+									assertTrueE(t, expectedTime.Equal(tc.actual.(map[string][]time.Time)[k][i]))
+								}
+							}
+						}
+					} else {
+						assertDeepEqualE(t, tc.actual, tc.expected)
+					}
+				})
+			}
+		})
+	})
+}
+
+func TestNullAndEmptyMaps(t *testing.T) {
+	skipStructuredTypesTestsOnGHActions(t)
+	runDBTest(t, func(dbt *DBTest) {
+		forAllStructureTypeFormats(dbt, func(t *testing.T, format string) {
+			rows := dbt.mustQuery("SELECT {'a': 1}::MAP(VARCHAR, INTEGER) UNION SELECT NULL UNION SELECT {}::MAP(VARCHAR, INTEGER) UNION SELECT {'d': 4}::MAP(VARCHAR, INTEGER)")
+			defer rows.Close()
+			checkRow := func(rows *RowsExtended, expected *map[string]int64) {
+				rows.Next()
+				var res *map[string]int64
+				err := rows.Scan(&res)
+				assertNilF(t, err)
+				assertDeepEqualE(t, res, expected)
+			}
+			checkRow(rows, &map[string]int64{"a": 1})
+			checkRow(rows, nil)
+			checkRow(rows, &map[string]int64{})
+			checkRow(rows, &map[string]int64{"d": 4})
+		})
+	})
+}
+
+func TestMapWithNullValues(t *testing.T) {
+	skipStructuredTypesTestsOnGHActions(t)
+	warsawTz, err := time.LoadLocation("Europe/Warsaw")
+	assertNilF(t, err)
+	testcases := []struct {
+		name     string
+		query    string
+		actual   any
+		expected any
+	}{
+		{
+			name:     "string",
+			query:    "SELECT object_construct_keep_null('x', 'abc', 'y', null)::MAP(VARCHAR, VARCHAR)",
+			actual:   make(map[string]sql.NullString),
+			expected: map[string]sql.NullString{"x": {Valid: true, String: "abc"}, "y": {Valid: false}},
+		},
+		{
+			name:     "bool",
+			query:    "SELECT object_construct_keep_null('x', true, 'y', null)::MAP(VARCHAR, BOOLEAN)",
+			actual:   make(map[string]sql.NullBool),
+			expected: map[string]sql.NullBool{"x": {Valid: true, Bool: true}, "y": {Valid: false}},
+		},
+		{
+			name:     "fixed - scale == 0",
+			query:    "SELECT object_construct_keep_null('x', 1, 'y', null)::MAP(VARCHAR, BIGINT)",
+			actual:   make(map[string]sql.NullInt64),
+			expected: map[string]sql.NullInt64{"x": {Valid: true, Int64: 1}, "y": {Valid: false}},
+		},
+		{
+			name:     "fixed - scale != 0",
+			query:    "SELECT object_construct_keep_null('x', 1.1, 'y', null)::MAP(VARCHAR, NUMBER(38, 19))",
+			actual:   make(map[string]sql.NullFloat64),
+			expected: map[string]sql.NullFloat64{"x": {Valid: true, Float64: 1.1}, "y": {Valid: false}},
+		},
+		{
+			name:     "real",
+			query:    "SELECT object_construct_keep_null('x', 1.1, 'y', null)::MAP(VARCHAR, DOUBLE)",
+			actual:   make(map[string]sql.NullFloat64),
+			expected: map[string]sql.NullFloat64{"x": {Valid: true, Float64: 1.1}, "y": {Valid: false}},
+		},
+		{
+			name:     "binary",
+			query:    "SELECT object_construct_keep_null('x', TO_BINARY('616263'), 'y', null)::MAP(VARCHAR, BINARY)",
+			actual:   make(map[string][]byte),
+			expected: map[string][]byte{"x": {'a', 'b', 'c'}, "y": nil},
+		},
+		{
+			name:     "date",
+			query:    "SELECT object_construct_keep_null('x', '2024-04-05'::DATE, 'y', null)::MAP(VARCHAR, DATE)",
+			actual:   make(map[string]sql.NullTime),
+			expected: map[string]sql.NullTime{"x": {Valid: true, Time: time.Date(2024, time.April, 5, 0, 0, 0, 0, time.UTC)}, "y": {Valid: false}},
+		},
+		{
+			name:     "time",
+			query:    "SELECT object_construct_keep_null('x', '13:14:15'::TIME, 'y', null)::MAP(VARCHAR, TIME)",
+			actual:   make(map[string]sql.NullTime),
+			expected: map[string]sql.NullTime{"x": {Valid: true, Time: time.Date(1, 0, 0, 13, 14, 15, 0, time.UTC)}, "y": {Valid: false}},
+		},
+		{
+			name:     "timestamp_tz",
+			query:    "SELECT object_construct_keep_null('x', '2022-08-31 13:43:22 +0200'::TIMESTAMP_TZ, 'y', null)::MAP(VARCHAR, TIMESTAMP_TZ)",
+			actual:   make(map[string]sql.NullTime),
+			expected: map[string]sql.NullTime{"x": {Valid: true, Time: time.Date(2022, 8, 31, 13, 43, 22, 0, warsawTz)}, "y": {Valid: false}},
+		},
+		{
+			name:     "timestamp_ntz",
+			query:    "SELECT object_construct_keep_null('x', '2022-08-31 13:43:22'::TIMESTAMP_NTZ, 'y', null)::MAP(VARCHAR, TIMESTAMP_NTZ)",
+			actual:   make(map[string]sql.NullTime),
+			expected: map[string]sql.NullTime{"x": {Valid: true, Time: time.Date(2022, 8, 31, 13, 43, 22, 0, time.UTC)}, "y": {Valid: false}},
+		},
+		{
+			name:     "timestamp_ltz",
+			query:    "SELECT object_construct_keep_null('x', '2022-08-31 13:43:22'::TIMESTAMP_LTZ, 'y', null)::MAP(VARCHAR, TIMESTAMP_LTZ)",
+			actual:   make(map[string]sql.NullTime),
+			expected: map[string]sql.NullTime{"x": {Valid: true, Time: time.Date(2022, 8, 31, 13, 43, 22, 0, warsawTz)}, "y": {Valid: false}},
+		},
+	}
+	runDBTest(t, func(dbt *DBTest) {
+		dbt.mustExec("ALTER SESSION SET TIMEZONE = 'Europe/Warsaw'")
+		forAllStructureTypeFormats(dbt, func(t *testing.T, format string) {
+			for _, tc := range testcases {
+				t.Run(tc.name, func(t *testing.T) {
+					rows := dbt.mustQueryContext(WithMapValuesNullable(context.Background()), tc.query)
+					defer rows.Close()
+					rows.Next()
+					err := rows.Scan(&tc.actual)
+					assertNilF(t, err)
+					if tc.name == "time" {
+						for i, nt := range tc.actual.(map[string]sql.NullTime) {
+							assertEqualE(t, nt.Valid, tc.expected.(map[string]sql.NullTime)[i].Valid)
+							assertEqualE(t, nt.Time.Hour(), tc.expected.(map[string]sql.NullTime)[i].Time.Hour())
+							assertEqualE(t, nt.Time.Minute(), tc.expected.(map[string]sql.NullTime)[i].Time.Minute())
+							assertEqualE(t, nt.Time.Second(), tc.expected.(map[string]sql.NullTime)[i].Time.Second())
+						}
+					} else if tc.name == "timestamp_tz" || tc.name == "timestamp_ltz" || tc.name == "timestamp_ntz" {
+						for i, nt := range tc.actual.(map[string]sql.NullTime) {
+							assertEqualE(t, nt.Valid, tc.expected.(map[string]sql.NullTime)[i].Valid)
+							assertTrueE(t, nt.Time.Equal(tc.expected.(map[string]sql.NullTime)[i].Time))
+						}
+					} else {
+						assertDeepEqualE(t, tc.actual, tc.expected)
+					}
+				})
+			}
+		})
+	})
+}
+
+type HigherPrecisionStruct struct {
+	i *big.Int
+	f *big.Float
+}
+
+func (hps *HigherPrecisionStruct) Scan(val any) error {
+	st := val.(StructuredObject)
+	var err error
+	if hps.i, err = st.GetBigInt("i"); err != nil {
+		return err
+	}
+	if hps.f, err = st.GetBigFloat("f"); err != nil {
+		return err
+	}
+	return nil
+}
+
 func TestWithHigherPrecision(t *testing.T) {
 	skipStructuredTypesTestsOnGHActions(t)
 	runDBTest(t, func(dbt *DBTest) {
@@ -937,135 +1345,6 @@ func TestWithHigherPrecision(t *testing.T) {
 	})
 }
 
-func TestNullAndEmptyMaps(t *testing.T) {
-	skipStructuredTypesTestsOnGHActions(t)
-	runDBTest(t, func(dbt *DBTest) {
-		forAllStructureTypeFormats(dbt, func(t *testing.T, format string) {
-			rows := dbt.mustQuery("SELECT {'a': 1}::MAP(VARCHAR, INTEGER) UNION SELECT NULL UNION SELECT {}::MAP(VARCHAR, INTEGER) UNION SELECT {'d': 4}::MAP(VARCHAR, INTEGER)")
-			defer rows.Close()
-			checkRow := func(rows *RowsExtended, expected *map[string]int64) {
-				rows.Next()
-				var res *map[string]int64
-				err := rows.Scan(&res)
-				assertNilF(t, err)
-				assertDeepEqualE(t, res, expected)
-			}
-			checkRow(rows, &map[string]int64{"a": 1})
-			checkRow(rows, nil)
-			checkRow(rows, &map[string]int64{})
-			checkRow(rows, &map[string]int64{"d": 4})
-		})
-	})
-}
-
-func TestMapWithNullValues(t *testing.T) {
-	skipStructuredTypesTestsOnGHActions(t)
-	warsawTz, err := time.LoadLocation("Europe/Warsaw")
-	assertNilF(t, err)
-	testcases := []struct {
-		name     string
-		query    string
-		actual   any
-		expected any
-	}{
-		{
-			name:     "string",
-			query:    "SELECT object_construct_keep_null('x', 'abc', 'y', null)::MAP(VARCHAR, VARCHAR)",
-			actual:   make(map[string]sql.NullString),
-			expected: map[string]sql.NullString{"x": {Valid: true, String: "abc"}, "y": {Valid: false}},
-		},
-		{
-			name:     "bool",
-			query:    "SELECT object_construct_keep_null('x', true, 'y', null)::MAP(VARCHAR, BOOLEAN)",
-			actual:   make(map[string]sql.NullBool),
-			expected: map[string]sql.NullBool{"x": {Valid: true, Bool: true}, "y": {Valid: false}},
-		},
-		{
-			name:     "fixed - scale == 0",
-			query:    "SELECT object_construct_keep_null('x', 1, 'y', null)::MAP(VARCHAR, BIGINT)",
-			actual:   make(map[string]sql.NullInt64),
-			expected: map[string]sql.NullInt64{"x": {Valid: true, Int64: 1}, "y": {Valid: false}},
-		},
-		{
-			name:     "fixed - scale != 0",
-			query:    "SELECT object_construct_keep_null('x', 1.1, 'y', null)::MAP(VARCHAR, NUMBER(38, 19))",
-			actual:   make(map[string]sql.NullFloat64),
-			expected: map[string]sql.NullFloat64{"x": {Valid: true, Float64: 1.1}, "y": {Valid: false}},
-		},
-		{
-			name:     "fixed - real",
-			query:    "SELECT object_construct_keep_null('x', 1.1, 'y', null)::MAP(VARCHAR, DOUBLE)",
-			actual:   make(map[string]sql.NullFloat64),
-			expected: map[string]sql.NullFloat64{"x": {Valid: true, Float64: 1.1}, "y": {Valid: false}},
-		},
-		{
-			name:     "binary",
-			query:    "SELECT object_construct_keep_null('x', TO_BINARY('616263'), 'y', null)::MAP(VARCHAR, BINARY)",
-			actual:   make(map[string][]byte),
-			expected: map[string][]byte{"x": {'a', 'b', 'c'}, "y": nil},
-		},
-		{
-			name:     "date",
-			query:    "SELECT object_construct_keep_null('x', '2024-04-05'::DATE, 'y', null)::MAP(VARCHAR, DATE)",
-			actual:   make(map[string]sql.NullTime),
-			expected: map[string]sql.NullTime{"x": {Valid: true, Time: time.Date(2024, time.April, 5, 0, 0, 0, 0, time.UTC)}, "y": {Valid: false}},
-		},
-		{
-			name:     "time",
-			query:    "SELECT object_construct_keep_null('x', '13:14:15'::TIME, 'y', null)::MAP(VARCHAR, TIME)",
-			actual:   make(map[string]sql.NullTime),
-			expected: map[string]sql.NullTime{"x": {Valid: true, Time: time.Date(1, 0, 0, 13, 14, 15, 0, time.UTC)}, "y": {Valid: false}},
-		},
-		{
-			name:     "timestamp_tz",
-			query:    "SELECT object_construct_keep_null('x', '2022-08-31 13:43:22 +0200'::TIMESTAMP_TZ, 'y', null)::MAP(VARCHAR, TIMESTAMP_TZ)",
-			actual:   make(map[string]sql.NullTime),
-			expected: map[string]sql.NullTime{"x": {Valid: true, Time: time.Date(2022, 8, 31, 13, 43, 22, 0, warsawTz)}, "y": {Valid: false}},
-		},
-		{
-			name:     "timestamp_ntz",
-			query:    "SELECT object_construct_keep_null('x', '2022-08-31 13:43:22'::TIMESTAMP_NTZ, 'y', null)::MAP(VARCHAR, TIMESTAMP_NTZ)",
-			actual:   make(map[string]sql.NullTime),
-			expected: map[string]sql.NullTime{"x": {Valid: true, Time: time.Date(2022, 8, 31, 13, 43, 22, 0, time.UTC)}, "y": {Valid: false}},
-		},
-		{
-			name:     "timestamp_tz",
-			query:    "SELECT object_construct_keep_null('x', '2022-08-31 13:43:22'::TIMESTAMP_LTZ, 'y', null)::MAP(VARCHAR, TIMESTAMP_LTZ)",
-			actual:   make(map[string]sql.NullTime),
-			expected: map[string]sql.NullTime{"x": {Valid: true, Time: time.Date(2022, 8, 31, 13, 43, 22, 0, warsawTz)}, "y": {Valid: false}},
-		},
-	}
-	runDBTest(t, func(dbt *DBTest) {
-		dbt.mustExec("ALTER SESSION SET TIMEZONE = 'Europe/Warsaw'")
-		forAllStructureTypeFormats(dbt, func(t *testing.T, format string) {
-			for _, tc := range testcases {
-				t.Run(tc.name, func(t *testing.T) {
-					rows := dbt.mustQueryContext(WithMapValuesNullable(context.Background()), tc.query)
-					defer rows.Close()
-					rows.Next()
-					err := rows.Scan(&tc.actual)
-					assertNilF(t, err)
-					if tc.name == "time" {
-						for i, nt := range tc.actual.(map[string]sql.NullTime) {
-							assertEqualE(t, nt.Valid, tc.expected.(map[string]sql.NullTime)[i].Valid)
-							assertEqualE(t, nt.Time.Hour(), tc.expected.(map[string]sql.NullTime)[i].Time.Hour())
-							assertEqualE(t, nt.Time.Minute(), tc.expected.(map[string]sql.NullTime)[i].Time.Minute())
-							assertEqualE(t, nt.Time.Second(), tc.expected.(map[string]sql.NullTime)[i].Time.Second())
-						}
-					} else if tc.name == "timestamp_tz" || tc.name == "timestamp_ltz" || tc.name == "timestamp_ntz" {
-						for i, nt := range tc.actual.(map[string]sql.NullTime) {
-							assertEqualE(t, nt.Valid, tc.expected.(map[string]sql.NullTime)[i].Valid)
-							assertTrueE(t, nt.Time.Equal(tc.expected.(map[string]sql.NullTime)[i].Time))
-						}
-					} else {
-						assertDeepEqualE(t, tc.actual, tc.expected)
-					}
-				})
-			}
-		})
-	})
-}
-
 func forAllStructureTypeFormats(dbt *DBTest, f func(t *testing.T, format string)) {
 	for _, tc := range []struct {
 		name        string