1515package functions
1616
1717import (
18- "fmt"
19-
18+ "github.com/cockroachdb/errors"
2019 "github.com/dolthub/go-mysql-server/sql"
2120
21+ "github.com/dolthub/doltgresql/core"
22+ "github.com/dolthub/doltgresql/core/id"
23+ "github.com/dolthub/doltgresql/utils"
24+
2225 "github.com/dolthub/doltgresql/server/functions/framework"
2326 pgtypes "github.com/dolthub/doltgresql/server/types"
2427)
@@ -40,7 +43,7 @@ var record_in = framework.Function3{
4043 Parameters : [3 ]* pgtypes.DoltgresType {pgtypes .Cstring , pgtypes .Oid , pgtypes .Int32 },
4144 Strict : true ,
4245 Callable : func (ctx * sql.Context , _ [4 ]* pgtypes.DoltgresType , val1 , val2 , val3 any ) (any , error ) {
43- return nil , fmt .Errorf ("record_in not implemented" )
46+ return nil , errors .Errorf ("record_in not implemented" )
4447 },
4548}
4649
@@ -53,24 +56,66 @@ var record_out = framework.Function1{
5356 Callable : func (ctx * sql.Context , t [2 ]* pgtypes.DoltgresType , val any ) (any , error ) {
5457 values , ok := val .([]pgtypes.RecordValue )
5558 if ! ok {
56- return nil , fmt .Errorf ("expected []RecordValue, but got %T" , val )
59+ return nil , errors .Errorf ("expected []RecordValue, but got %T" , val )
5760 }
5861 return pgtypes .RecordToString (ctx , values )
5962 },
6063}
6164
62- // record_recv represents the PostgreSQL function of record type IO receive.
65+ // record_recv represents the PostgreSQL function of record type IO receive. The input of this function is expected to
66+ // be the output of record_send.
6367var record_recv = framework.Function3 {
6468 Name : "record_recv" ,
6569 Return : pgtypes .Record ,
6670 Parameters : [3 ]* pgtypes.DoltgresType {pgtypes .Internal , pgtypes .Oid , pgtypes .Int32 },
6771 Strict : true ,
6872 Callable : func (ctx * sql.Context , _ [4 ]* pgtypes.DoltgresType , val1 , val2 , val3 any ) (any , error ) {
69- return nil , fmt .Errorf ("record_recv not implemented" )
73+ data , ok := val1 .([]byte )
74+ if ! ok {
75+ return nil , errors .Errorf ("expected []byte, but got `%T`" , val1 )
76+ }
77+ typeColl , err := core .GetTypesCollectionFromContext (ctx )
78+ if err != nil {
79+ return nil , err
80+ }
81+ reader := utils .NewReader (data )
82+ version := reader .Byte ()
83+ switch version {
84+ case 0 :
85+ valuesLen := reader .VariableUint ()
86+ values := make ([]pgtypes.RecordValue , valuesLen )
87+ for i := uint64 (0 ); i < valuesLen ; i ++ {
88+ typeId := id .Type (reader .Id ())
89+ valueData := reader .ByteSlice ()
90+ dgtype , err := typeColl .GetType (ctx , typeId )
91+ if err != nil {
92+ return nil , err
93+ }
94+ if dgtype == nil {
95+ return nil , errors .Errorf ("record_recv encountered type `%s.%s` which could not be found" ,
96+ typeId .SchemaName (), typeId .TypeName ())
97+ }
98+ value , err := dgtype .DeserializeValue (ctx , valueData )
99+ if err != nil {
100+ return nil , err
101+ }
102+ values [i ] = pgtypes.RecordValue {
103+ Value : value ,
104+ Type : dgtype ,
105+ }
106+ }
107+ if reader .RemainingBytes () > 0 {
108+ return nil , errors .New ("record_recv encountered extra data during deserialization" )
109+ }
110+ return values , nil
111+ default :
112+ return nil , errors .Errorf ("version %d of record serialization is not supported, please upgrade the server" , version )
113+ }
70114 },
71115}
72116
73- // record_send represents the PostgreSQL function of record type IO send.
117+ // record_send represents the PostgreSQL function of record type IO send. The output of this function is expected to
118+ // be the input of record_recv.
74119var record_send = framework.Function1 {
75120 Name : "record_send" ,
76121 Return : pgtypes .Bytea ,
@@ -79,16 +124,24 @@ var record_send = framework.Function1{
79124 Callable : func (ctx * sql.Context , t [2 ]* pgtypes.DoltgresType , val any ) (any , error ) {
80125 values , ok := val .([]pgtypes.RecordValue )
81126 if ! ok {
82- return nil , fmt .Errorf ("expected []RecordValue, but got %T" , val )
127+ return nil , errors .Errorf ("expected []RecordValue, but got %T" , val )
83128 }
84- // TODO: converting from a string back to the record doesn't work as we lose type information, so we need to
85- // figure out how to retain this information
86- output , err := pgtypes .RecordToString (ctx , values )
87- if err != nil {
88- return nil , err
129+ writer := utils .NewWriter (uint64 (16 * len (values )))
130+ writer .Byte (0 ) // Version
131+ writer .VariableUint (uint64 (len (values )))
132+ for _ , value := range values {
133+ dgtype , ok := value .Type .(* pgtypes.DoltgresType )
134+ if ! ok {
135+ return nil , errors .Errorf ("record_send only supports Doltgres types, but received `%T`" , value .Type )
136+ }
137+ valBytes , err := dgtype .SerializeValue (ctx , value .Value )
138+ if err != nil {
139+ return nil , err
140+ }
141+ writer .Id (dgtype .ID .AsId ())
142+ writer .ByteSlice (valBytes )
89143 }
90-
91- return []byte (output .(string )), nil
144+ return writer .Data (), nil
92145 },
93146}
94147
0 commit comments