@@ -130,11 +130,9 @@ func GetExplicitCast(fromType *pgtypes.DoltgresType, toType *pgtypes.DoltgresTyp
130130 } else if tcf = getCast (implicitTypeCastMutex , implicitTypeCastsMap , fromType , toType , GetExplicitCast ); tcf != nil {
131131 return tcf
132132 }
133- // We check for the identity after checking the maps, as the identity may be overridden (such as for types that have
134- // parameters). If one of the types are a string type, then we do not use the identity, and use the I/O conversions
135- // below.
136- if fromType .ID == toType .ID && toType .TypCategory != pgtypes .TypeCategory_StringTypes && fromType .TypCategory != pgtypes .TypeCategory_StringTypes {
137- return IdentityCast
133+ // We check for the identity and sizing casts after checking the maps, as the identity may be overridden by a user.
134+ if cast := getSizingOrIdentityCast (fromType , toType , true ); cast != nil {
135+ return cast
138136 }
139137 // All types have a built-in explicit cast from string types: https://www.postgresql.org/docs/15/sql-createcast.html
140138 if fromType .TypCategory == pgtypes .TypeCategory_StringTypes {
@@ -172,15 +170,11 @@ func GetAssignmentCast(fromType *pgtypes.DoltgresType, toType *pgtypes.DoltgresT
172170 } else if tcf = getCast (implicitTypeCastMutex , implicitTypeCastsMap , fromType , toType , GetAssignmentCast ); tcf != nil {
173171 return tcf
174172 }
175- // We check for the identity after checking the maps, as the identity may be overridden (such as for types that have
176- // parameters). If the "to" type is a string type, then we do not use the identity, and use the I/O conversion below.
177- if fromType .ID == toType .ID && fromType .TypCategory != pgtypes .TypeCategory_StringTypes && fromType .TypCategory != pgtypes .TypeCategory_BitStringTypes {
178- return IdentityCast
173+ // We check for the identity and sizing casts after checking the maps, as the identity may be overridden by a user.
174+ if cast := getSizingOrIdentityCast (fromType , toType , false ); cast != nil {
175+ return cast
179176 }
180-
181177 // All types have a built-in assignment cast to string types: https://www.postgresql.org/docs/15/sql-createcast.html
182- // This is also where length checks occur for types like char(n), varchar(n), bit(n), etc., which is not great
183- // TODO: move length checks to their own analyzer step
184178 if toType .TypCategory == pgtypes .TypeCategory_StringTypes {
185179 return func (ctx * sql.Context , val any , targetType * pgtypes.DoltgresType ) (any , error ) {
186180 if val == nil {
@@ -192,17 +186,6 @@ func GetAssignmentCast(fromType *pgtypes.DoltgresType, toType *pgtypes.DoltgresT
192186 }
193187 return targetType .IoInput (ctx , str )
194188 }
195- } else if toType .TypCategory == pgtypes .TypeCategory_BitStringTypes {
196- return func (ctx * sql.Context , val any , targetType * pgtypes.DoltgresType ) (any , error ) {
197- if val == nil {
198- return nil , nil
199- }
200- str , err := fromType .IoOutput (ctx , val )
201- if err != nil {
202- return nil , err
203- }
204- return targetType .IoInput (ctx , str )
205- }
206189 }
207190 return nil
208191}
@@ -213,10 +196,9 @@ func GetImplicitCast(fromType *pgtypes.DoltgresType, toType *pgtypes.DoltgresTyp
213196 if tcf := getCast (implicitTypeCastMutex , implicitTypeCastsMap , fromType , toType , GetImplicitCast ); tcf != nil {
214197 return tcf
215198 }
216- // We check for the identity after checking the maps, as the identity may be overridden (such as for types that have
217- // parameters).
218- if fromType .ID == toType .ID {
219- return IdentityCast
199+ // We check for the identity and sizing casts after checking the maps, as the identity may be overridden by a user.
200+ if cast := getSizingOrIdentityCast (fromType , toType , false ); cast != nil {
201+ return cast
220202 }
221203 return nil
222204}
@@ -296,6 +278,40 @@ func getCast(mutex *sync.RWMutex,
296278 return nil
297279}
298280
281+ // getSizingOrIdentityCast returns an identity cast if the two types are exactly the same, and a sizing cast if they
282+ // only differ in their atttypmod values. Returns nil if no functions are matched. This mirrors the behavior as described in:
283+ // https://www.postgresql.org/docs/15/typeconv-query.html
284+ func getSizingOrIdentityCast (fromType * pgtypes.DoltgresType , toType * pgtypes.DoltgresType , isExplicitCast bool ) pgtypes.TypeCastFunction {
285+ // If we receive different types, then we can return immediately
286+ if fromType .ID != toType .ID {
287+ return nil
288+ }
289+ // If we have different atttypmod values, then we need to do a sizing cast only if one exists
290+ if fromType .GetAttTypMod () != toType .GetAttTypMod () {
291+ // TODO: We don't have any sizing cast functions implemented, so for now we'll approximate using output to input.
292+ // We can use the query below to find all implemented sizing cast functions. It's also detailed in the link above.
293+ // Lastly, not all sizing functions accept a boolean, but for those that do, we need to see whether true is
294+ // used for explicit casts, or whether true is used for implicit casts.
295+ // SELECT
296+ // format_type(c.castsource, NULL) AS source,
297+ // format_type(c.casttarget, NULL) AS target,
298+ // p.oid::regprocedure AS func
299+ // FROM pg_cast c JOIN pg_proc p ON p.oid = c.castfunc WHERE c.castsource = c.casttarget ORDER BY 1,2;
300+ return func (ctx * sql.Context , val any , targetType * pgtypes.DoltgresType ) (any , error ) {
301+ if val == nil {
302+ return nil , nil
303+ }
304+ str , err := fromType .IoOutput (ctx , val )
305+ if err != nil {
306+ return nil , err
307+ }
308+ return targetType .IoInput (ctx , str )
309+ }
310+ }
311+ // If there is no sizing cast, then we simply use the identity cast
312+ return IdentityCast
313+ }
314+
299315// IdentityCast returns the input value.
300316func IdentityCast (ctx * sql.Context , val any , targetType * pgtypes.DoltgresType ) (any , error ) {
301317 return val , nil
0 commit comments