@@ -169,34 +169,7 @@ var array_recv = framework.Function3{
169169 baseType := pgtypes .IDToBuiltInDoltgresType [id .Type (baseTypeOid )]
170170 typmod := val3 .(int32 )
171171 baseType = baseType .WithAttTypMod (typmod )
172- // Check for the nil value, then ensure the minimum length of the slice
173- if len (data ) == 0 {
174- return nil , nil
175- }
176- if len (data ) < 4 {
177- return nil , errors .Errorf ("deserializing non-nil array value has invalid length of %d" , len (data ))
178- }
179- // Grab the number of elements and construct an output slice of the appropriate size
180- elementCount := binary .LittleEndian .Uint32 (data )
181- output := make ([]any , elementCount )
182- // Read all elements
183- for i := uint32 (0 ); i < elementCount ; i ++ {
184- // We read from i+1 to account for the element count at the beginning
185- offset := binary .LittleEndian .Uint32 (data [(i + 1 )* 4 :])
186- // If the value is null, then we can skip it, since the output slice default initializes all values to nil
187- if data [offset ] == 1 {
188- continue
189- }
190- // The element data is everything from the offset to the next offset, excluding the null determinant
191- nextOffset := binary .LittleEndian .Uint32 (data [(i + 2 )* 4 :])
192- o , err := baseType .DeserializeValue (ctx , data [offset + 1 :nextOffset ])
193- if err != nil {
194- return nil , err
195- }
196- output [i ] = o
197- }
198- // Returns all read elements
199- return output , nil
172+ return deserializeArray (ctx , data , baseType )
200173 },
201174}
202175
@@ -207,49 +180,9 @@ var array_send = framework.Function1{
207180 Parameters : [1 ]* pgtypes.DoltgresType {pgtypes .AnyArray },
208181 Strict : true ,
209182 Callable : func (ctx * sql.Context , t [2 ]* pgtypes.DoltgresType , val any ) (any , error ) {
210- arrType := t [0 ]
211- baseType := arrType .ArrayBaseType ()
212183 vals := val .([]any )
213-
214- bb := bytes.Buffer {}
215- // Write the element count to a buffer. We're using an array since it's stack-allocated, so no need for pooling.
216- var elementCount [4 ]byte
217- binary .LittleEndian .PutUint32 (elementCount [:], uint32 (len (vals )))
218- bb .Write (elementCount [:])
219- // Create an array that contains the offsets for each value. Since we can't update the offset portion of the buffer
220- // as we determine the offsets, we have to track them outside the buffer. We'll overwrite the buffer later with the
221- // correct offsets. The last offset represents the end of the slice, which simplifies the logic for reading elements
222- // using the "current offset to next offset" strategy. We use a byte slice since the buffer only works with byte
223- // slices.
224- offsets := make ([]byte , (len (vals )+ 1 )* 4 )
225- bb .Write (offsets )
226- // The starting offset for the first element is Count(uint32) + (NumberOfElementOffsets * sizeof(uint32))
227- currentOffset := uint32 (4 + (len (vals )+ 1 )* 4 )
228- for i := range vals {
229- // Write the current offset
230- binary .LittleEndian .PutUint32 (offsets [i * 4 :], currentOffset )
231- // Handle serialization of the value
232- // TODO: ARRAYs may be multidimensional, such as ARRAY[[4,2],[6,3]], which isn't accounted for here
233- serializedVal , err := baseType .SerializeValue (ctx , vals [i ])
234- if err != nil {
235- return nil , err
236- }
237- // Handle the nil case and non-nil case
238- if serializedVal == nil {
239- bb .WriteByte (1 )
240- currentOffset += 1
241- } else {
242- bb .WriteByte (0 )
243- bb .Write (serializedVal )
244- currentOffset += 1 + uint32 (len (serializedVal ))
245- }
246- }
247- // Write the final offset, which will equal the length of the serialized slice
248- binary .LittleEndian .PutUint32 (offsets [len (offsets )- 4 :], currentOffset )
249- // Get the final output, and write the updated offsets to it
250- outputBytes := bb .Bytes ()
251- copy (outputBytes [4 :], offsets )
252- return outputBytes , nil
184+ arrType := t [0 ]
185+ return serializeArray (ctx , vals , arrType .ArrayBaseType ())
253186 },
254187}
255188
@@ -301,3 +234,78 @@ var array_subscript_handler = framework.Function1{
301234 return []byte {}, nil
302235 },
303236}
237+
238+ // deserializeArray serializes an array of given base type.
239+ func serializeArray (ctx * sql.Context , vals []any , baseType * pgtypes.DoltgresType ) ([]byte , error ) {
240+ bb := bytes.Buffer {}
241+ // Write the element count to a buffer. We're using an array since it's stack-allocated, so no need for pooling.
242+ var elementCount [4 ]byte
243+ binary .LittleEndian .PutUint32 (elementCount [:], uint32 (len (vals )))
244+ bb .Write (elementCount [:])
245+ // Create an array that contains the offsets for each value. Since we can't update the offset portion of the buffer
246+ // as we determine the offsets, we have to track them outside the buffer. We'll overwrite the buffer later with the
247+ // correct offsets. The last offset represents the end of the slice, which simplifies the logic for reading elements
248+ // using the "current offset to next offset" strategy. We use a byte slice since the buffer only works with byte
249+ // slices.
250+ offsets := make ([]byte , (len (vals )+ 1 )* 4 )
251+ bb .Write (offsets )
252+ // The starting offset for the first element is Count(uint32) + (NumberOfElementOffsets * sizeof(uint32))
253+ currentOffset := uint32 (4 + (len (vals )+ 1 )* 4 )
254+ for i := range vals {
255+ // Write the current offset
256+ binary .LittleEndian .PutUint32 (offsets [i * 4 :], currentOffset )
257+ // Handle serialization of the value
258+ // TODO: ARRAYs may be multidimensional, such as ARRAY[[4,2],[6,3]], which isn't accounted for here
259+ serializedVal , err := baseType .SerializeValue (ctx , vals [i ])
260+ if err != nil {
261+ return nil , err
262+ }
263+ // Handle the nil case and non-nil case
264+ if serializedVal == nil {
265+ bb .WriteByte (1 )
266+ currentOffset += 1
267+ } else {
268+ bb .WriteByte (0 )
269+ bb .Write (serializedVal )
270+ currentOffset += 1 + uint32 (len (serializedVal ))
271+ }
272+ }
273+ // Write the final offset, which will equal the length of the serialized slice
274+ binary .LittleEndian .PutUint32 (offsets [len (offsets )- 4 :], currentOffset )
275+ // Get the final output, and write the updated offsets to it
276+ outputBytes := bb .Bytes ()
277+ copy (outputBytes [4 :], offsets )
278+ return outputBytes , nil
279+ }
280+
281+ // deserializeArray deserializes an array of given base type.
282+ func deserializeArray (ctx * sql.Context , data []byte , baseType * pgtypes.DoltgresType ) ([]any , error ) {
283+ // Check for the nil value, then ensure the minimum length of the slice
284+ if len (data ) == 0 {
285+ return nil , nil
286+ }
287+ if len (data ) < 4 {
288+ return nil , errors .Errorf ("deserializing non-nil array value has invalid length of %d" , len (data ))
289+ }
290+ // Grab the number of elements and construct an output slice of the appropriate size
291+ elementCount := binary .LittleEndian .Uint32 (data )
292+ output := make ([]any , elementCount )
293+ // Read all elements
294+ for i := uint32 (0 ); i < elementCount ; i ++ {
295+ // We read from i+1 to account for the element count at the beginning
296+ offset := binary .LittleEndian .Uint32 (data [(i + 1 )* 4 :])
297+ // If the value is null, then we can skip it, since the output slice default initializes all values to nil
298+ if data [offset ] == 1 {
299+ continue
300+ }
301+ // The element data is everything from the offset to the next offset, excluding the null determinant
302+ nextOffset := binary .LittleEndian .Uint32 (data [(i + 2 )* 4 :])
303+ o , err := baseType .DeserializeValue (ctx , data [offset + 1 :nextOffset ])
304+ if err != nil {
305+ return nil , err
306+ }
307+ output [i ] = o
308+ }
309+ // Returns all read elements
310+ return output , nil
311+ }
0 commit comments