@@ -50,15 +50,24 @@ cdef class Codec:
5050 self .encoder = < codec_encode_func> & self .encode_scalar
5151 self .decoder = < codec_decode_func> & self .decode_scalar
5252 elif type == CODEC_ARRAY:
53- self .encoder = < codec_encode_func> & self .encode_array
5453 if format == PG_FORMAT_BINARY:
54+ self .encoder = < codec_encode_func> & self .encode_array
5555 self .decoder = < codec_decode_func> & self .decode_array
5656 else :
57+ self .encoder = < codec_encode_func> & self .encode_array_text
5758 self .decoder = < codec_decode_func> & self .decode_array_text
5859 elif type == CODEC_RANGE:
60+ if format != PG_FORMAT_BINARY:
61+ raise RuntimeError (
62+ ' cannot encode type "{}"."{}": text encoding of '
63+ ' range types is not supported' .format(schema, name))
5964 self .encoder = < codec_encode_func> & self .encode_range
6065 self .decoder = < codec_decode_func> & self .decode_range
6166 elif type == CODEC_COMPOSITE:
67+ if format != PG_FORMAT_BINARY:
68+ raise RuntimeError (
69+ ' cannot encode type "{}"."{}": text encoding of '
70+ ' composite types is not supported' .format(schema, name))
6271 self .encoder = < codec_encode_func> & self .encode_composite
6372 self .decoder = < codec_decode_func> & self .decode_composite
6473 elif type == CODEC_PY:
@@ -91,6 +100,13 @@ cdef class Codec:
91100 codec_encode_func_ex,
92101 < void * > (< cpython.PyObject> self .element_codec))
93102
103+ cdef encode_array_text(self , ConnectionSettings settings, WriteBuffer buf,
104+ object obj):
105+ return textarray_encode(settings, buf, obj,
106+ codec_encode_func_ex,
107+ < void * > (< cpython.PyObject> self .element_codec),
108+ self .element_delimiter)
109+
94110 cdef encode_range(self , ConnectionSettings settings, WriteBuffer buf,
95111 object obj):
96112 range_encode(settings, buf, obj, self .element_codec.oid,
@@ -269,22 +285,22 @@ cdef class Codec:
269285 Codec element_codec):
270286 cdef Codec codec
271287 codec = Codec(oid)
272- codec.init(name, schema, ' range' , CODEC_RANGE, PG_FORMAT_BINARY,
273- NULL , NULL , None , None , element_codec, None , None , None ,
274- 0 )
288+ codec.init(name, schema, ' range' , CODEC_RANGE, element_codec.format,
289+ NULL , NULL , None , None , element_codec, None , None , None , 0 )
275290 return codec
276291
277292 @staticmethod
278293 cdef Codec new_composite_codec(uint32_t oid,
279294 str name,
280295 str schema,
296+ CodecFormat format,
281297 list element_codecs,
282298 tuple element_type_oids,
283299 object element_names):
284300 cdef Codec codec
285301 codec = Codec(oid)
286302 codec.init(name, schema, ' composite' , CODEC_COMPOSITE,
287- PG_FORMAT_BINARY , NULL , NULL , None , None , None ,
303+ format , NULL , NULL , None , None , None ,
288304 element_type_oids, element_names, element_codecs, 0 )
289305 return codec
290306
@@ -368,11 +384,12 @@ cdef class DataCodecConfig:
368384 elem_format = PG_FORMAT_BINARY
369385 else :
370386 elem_format = PG_FORMAT_TEXT
387+
371388 elem_codec = self .get_codec(array_element_oid, elem_format)
372389 if elem_codec is None :
373- raise RuntimeError (
374- ' no codec for array element type {} ' .format (
375- array_element_oid) )
390+ elem_format = PG_FORMAT_TEXT
391+ elem_codec = self .declare_fallback_codec (
392+ array_element_oid, name, schema )
376393
377394 elem_delim = < Py_UCS4> ti[' elemdelim' ][0 ]
378395
@@ -410,9 +427,8 @@ cdef class DataCodecConfig:
410427
411428 self ._type_codecs_cache[oid, format] = \
412429 Codec.new_composite_codec(
413- oid, name, schema, comp_elem_codecs,
414- comp_type_attrs,
415- element_names)
430+ oid, name, schema, format, comp_elem_codecs,
431+ comp_type_attrs, element_names)
416432
417433 elif ti[' kind' ] == b' d' :
418434 # Domain type
@@ -424,8 +440,9 @@ cdef class DataCodecConfig:
424440
425441 elem_codec = self .get_codec(base_type, format)
426442 if elem_codec is None :
427- raise RuntimeError (
428- ' no codec for domain base type {}' .format(base_type))
443+ format = PG_FORMAT_TEXT
444+ elem_codec = self .declare_fallback_codec(
445+ base_type, name, schema)
429446
430447 self ._type_codecs_cache[oid, format] = elem_codec
431448
@@ -441,34 +458,18 @@ cdef class DataCodecConfig:
441458 elem_format = PG_FORMAT_BINARY
442459 else :
443460 elem_format = PG_FORMAT_TEXT
461+
444462 elem_codec = self .get_codec(range_subtype_oid, elem_format)
445463 if elem_codec is None :
446- raise RuntimeError (
447- ' no codec for range element type {} ' .format (
448- range_subtype_oid) )
464+ elem_format = PG_FORMAT_TEXT
465+ elem_codec = self .declare_fallback_codec (
466+ range_subtype_oid, name, schema )
449467
450468 self ._type_codecs_cache[oid, elem_format] = \
451469 Codec.new_range_codec(oid, name, schema, elem_codec)
452470
453471 else :
454- if oid <= MAXBUILTINOID:
455- # This is a non-BKI type, for which ayncpg has no
456- # defined codec. This should only happen for newly
457- # added builtin types, for which this version of
458- # asyncpg is lacking support.
459- #
460- raise NotImplementedError (
461- ' unhandled standard data type {!r} (OID {})' .format(
462- name, oid))
463- else :
464- # This is a non-BKI type, and as such, has no
465- # stable OID, so no possibility of a builtin codec.
466- # In this case, fallback to text format. Applications
467- # can avoid this by specifying a codec for this type
468- # using Connection.set_type_codec().
469- #
470- self .set_builtin_type_codec(oid, name, schema, ' scalar' ,
471- UNKNOWNOID)
472+ self .declare_fallback_codec(oid, name, schema)
472473
473474 def add_python_codec (self , typeoid , typename , typeschema , typekind ,
474475 encoder , decoder , binary ):
@@ -478,13 +479,20 @@ cdef class DataCodecConfig:
478479 Codec.new_python_codec(typeoid, typename, typeschema, typekind,
479480 encoder, decoder, format)
480481
482+ self .clear_type_cache()
483+
481484 def set_builtin_type_codec (self , typeoid , typename , typeschema , typekind ,
482- alias_to ):
485+ alias_to , format = PG_FORMAT_ANY ):
483486 cdef:
484487 Codec codec
485488 Codec target_codec
486489
487- for format in (PG_FORMAT_BINARY, PG_FORMAT_TEXT):
490+ if format == PG_FORMAT_ANY:
491+ formats = (PG_FORMAT_BINARY, PG_FORMAT_TEXT)
492+ else :
493+ formats = (format,)
494+
495+ for format in formats:
488496 if self .get_codec(typeoid, format) is not None :
489497 raise ValueError (' cannot override codec for type {}' .format(
490498 typeoid))
@@ -509,9 +517,41 @@ cdef class DataCodecConfig:
509517 (typeoid, PG_FORMAT_TEXT) not in self ._local_type_codecs):
510518 raise ValueError (' unknown alias target: {}' .format(alias_to))
511519
520+ self .clear_type_cache()
521+
512522 def clear_type_cache (self ):
513523 self ._type_codecs_cache.clear()
514524
525+ def declare_fallback_codec (self , uint32_t oid , str name , str schema ):
526+ cdef Codec codec
527+
528+ codec = self .get_codec(oid, PG_FORMAT_TEXT)
529+ if codec is not None :
530+ return codec
531+
532+ if oid <= MAXBUILTINOID:
533+ # This is a BKI type, for which ayncpg has no
534+ # defined codec. This should only happen for newly
535+ # added builtin types, for which this version of
536+ # asyncpg is lacking support.
537+ #
538+ raise NotImplementedError (
539+ ' unhandled standard data type {!r} (OID {})' .format(
540+ name, oid))
541+ else :
542+ # This is a non-BKI type, and as such, has no
543+ # stable OID, so no possibility of a builtin codec.
544+ # In this case, fallback to text format. Applications
545+ # can avoid this by specifying a codec for this type
546+ # using Connection.set_type_codec().
547+ #
548+ self .set_builtin_type_codec(oid, name, schema, ' scalar' ,
549+ TEXTOID, PG_FORMAT_TEXT)
550+
551+ codec = self .get_codec(oid, PG_FORMAT_TEXT)
552+
553+ return codec
554+
515555 cdef inline Codec get_codec(self , uint32_t oid, CodecFormat format):
516556 cdef Codec codec
517557
0 commit comments