In psycopg2 it was possible to covert NaN values to nulls, using adapters like this:
__REGISTERED = False
def _nan_to_null(f,
_NULL=psycopg2.extensions.AsIs('NULL'),
_Float=psycopg2.extensions.Float):
if not np.isnan(f):
return _Float(f)
return _NULL
def register_nan_adapter():
global __REGISTERED
if not __REGISTERED:
print('Register nan to null adapter for psycopg2...')
psycopg2.extensions.register_adapter(float, _nan_to_null)
__REGISTERED = True
else:
print('nan to null adapter for psycopg2 is already registered!')
How can the same goal be achieved in psycopg3?
The docs say you can create a Dumper for each datatype, so I did this at first:
class NullNan(FloatDumper):
def dump(self, elem):
if np.isnan(elem):
return b"NULL"
else:
return super().dump(elem)
connection.adapters.register_dumper(float, NullNan)
But this does not work. Returning just None does not help neither.
Update after Adrian Clavier answer:
It seems that having the dumper to return None is fine for inserts and updates, but will not work with a COPY:
class NullNan(FloatDumper):
def dump(self, elem):
if np.isnan(elem):
return None
else:
return super().dump(elem)
with cursor.copy(f"COPY _r ({col_names_str}) FROM STDIN") as copy:
for index, row in df.iterrows():
rec = row.values.tolist()
copy.write_row(rec)
Leads to exception:
psycopg.errors.QueryCanceled: COPY from stdin failed: error from Python: TypeError - expected string or bytes-like object