@@ -50,15 +50,24 @@ cdef class Codec:
50
50
self .encoder = < codec_encode_func> & self .encode_scalar
51
51
self .decoder = < codec_decode_func> & self .decode_scalar
52
52
elif type == CODEC_ARRAY:
53
- self .encoder = < codec_encode_func> & self .encode_array
54
53
if format == PG_FORMAT_BINARY:
54
+ self .encoder = < codec_encode_func> & self .encode_array
55
55
self .decoder = < codec_decode_func> & self .decode_array
56
56
else :
57
+ self .encoder = < codec_encode_func> & self .encode_array_text
57
58
self .decoder = < codec_decode_func> & self .decode_array_text
58
59
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))
59
64
self .encoder = < codec_encode_func> & self .encode_range
60
65
self .decoder = < codec_decode_func> & self .decode_range
61
66
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))
62
71
self .encoder = < codec_encode_func> & self .encode_composite
63
72
self .decoder = < codec_decode_func> & self .decode_composite
64
73
elif type == CODEC_PY:
@@ -91,6 +100,13 @@ cdef class Codec:
91
100
codec_encode_func_ex,
92
101
< void * > (< cpython.PyObject> self .element_codec))
93
102
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
+
94
110
cdef encode_range(self , ConnectionSettings settings, WriteBuffer buf,
95
111
object obj):
96
112
range_encode(settings, buf, obj, self .element_codec.oid,
@@ -269,22 +285,22 @@ cdef class Codec:
269
285
Codec element_codec):
270
286
cdef Codec codec
271
287
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 )
275
290
return codec
276
291
277
292
@staticmethod
278
293
cdef Codec new_composite_codec(uint32_t oid,
279
294
str name,
280
295
str schema,
296
+ CodecFormat format,
281
297
list element_codecs,
282
298
tuple element_type_oids,
283
299
object element_names):
284
300
cdef Codec codec
285
301
codec = Codec(oid)
286
302
codec.init(name, schema, ' composite' , CODEC_COMPOSITE,
287
- PG_FORMAT_BINARY , NULL , NULL , None , None , None ,
303
+ format , NULL , NULL , None , None , None ,
288
304
element_type_oids, element_names, element_codecs, 0 )
289
305
return codec
290
306
@@ -368,11 +384,12 @@ cdef class DataCodecConfig:
368
384
elem_format = PG_FORMAT_BINARY
369
385
else :
370
386
elem_format = PG_FORMAT_TEXT
387
+
371
388
elem_codec = self .get_codec(array_element_oid, elem_format)
372
389
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 )
376
393
377
394
elem_delim = < Py_UCS4> ti[' elemdelim' ][0 ]
378
395
@@ -410,9 +427,8 @@ cdef class DataCodecConfig:
410
427
411
428
self ._type_codecs_cache[oid, format] = \
412
429
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)
416
432
417
433
elif ti[' kind' ] == b' d' :
418
434
# Domain type
@@ -424,8 +440,9 @@ cdef class DataCodecConfig:
424
440
425
441
elem_codec = self .get_codec(base_type, format)
426
442
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)
429
446
430
447
self ._type_codecs_cache[oid, format] = elem_codec
431
448
@@ -441,34 +458,18 @@ cdef class DataCodecConfig:
441
458
elem_format = PG_FORMAT_BINARY
442
459
else :
443
460
elem_format = PG_FORMAT_TEXT
461
+
444
462
elem_codec = self .get_codec(range_subtype_oid, elem_format)
445
463
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 )
449
467
450
468
self ._type_codecs_cache[oid, elem_format] = \
451
469
Codec.new_range_codec(oid, name, schema, elem_codec)
452
470
453
471
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)
472
473
473
474
def add_python_codec (self , typeoid , typename , typeschema , typekind ,
474
475
encoder , decoder , binary ):
@@ -478,13 +479,20 @@ cdef class DataCodecConfig:
478
479
Codec.new_python_codec(typeoid, typename, typeschema, typekind,
479
480
encoder, decoder, format)
480
481
482
+ self .clear_type_cache()
483
+
481
484
def set_builtin_type_codec (self , typeoid , typename , typeschema , typekind ,
482
- alias_to ):
485
+ alias_to , format = PG_FORMAT_ANY ):
483
486
cdef:
484
487
Codec codec
485
488
Codec target_codec
486
489
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:
488
496
if self .get_codec(typeoid, format) is not None :
489
497
raise ValueError (' cannot override codec for type {}' .format(
490
498
typeoid))
@@ -509,9 +517,41 @@ cdef class DataCodecConfig:
509
517
(typeoid, PG_FORMAT_TEXT) not in self ._local_type_codecs):
510
518
raise ValueError (' unknown alias target: {}' .format(alias_to))
511
519
520
+ self .clear_type_cache()
521
+
512
522
def clear_type_cache (self ):
513
523
self ._type_codecs_cache.clear()
514
524
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
+
515
555
cdef inline Codec get_codec(self , uint32_t oid, CodecFormat format):
516
556
cdef Codec codec
517
557
0 commit comments