Skip to content

Commit ee28218

Browse files
committed
Use statement cache for copy_records_to_table introspection
Fixes: #1327
1 parent db8ecc2 commit ee28218

2 files changed

Lines changed: 50 additions & 1 deletion

File tree

‎asyncpg/connection.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1116,7 +1116,7 @@ async def copy_records_to_table(self, table_name, *, records,
11161116
intro_query = 'SELECT {cols} FROM {tab} LIMIT 1'.format(
11171117
tab=tabname, cols=col_list)
11181118

1119-
intro_ps = await self.prepare(intro_query)
1119+
intro_ps = await self._prepare(intro_query, use_cache=True)
11201120

11211121
cond = self._format_copy_where(where)
11221122
opts = '(FORMAT binary)'

‎tests/test_copy.py‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,55 @@ async def record_generator():
700700
finally:
701701
await self.con.execute('DROP TABLE copytab_async')
702702

703+
async def test_copy_records_to_table_stmt_cache(self):
704+
# The introspection statement must be taken from the statement
705+
# cache, otherwise every call pays for an extra round-trip.
706+
await self.con.execute('''
707+
CREATE TABLE copytab_cache(a text, b int);
708+
''')
709+
710+
try:
711+
intro_query = 'SELECT "a", "b" FROM "copytab_cache" LIMIT 1'
712+
cache_key = (intro_query, asyncpg.Record, False)
713+
714+
res = await self.con.copy_records_to_table(
715+
'copytab_cache', records=[('a', 1)], columns=['a', 'b'])
716+
self.assertEqual(res, 'COPY 1')
717+
718+
entry = self.con._stmt_cache.get(cache_key, promote=False)
719+
self.assertIsNotNone(entry)
720+
721+
res = await self.con.copy_records_to_table(
722+
'copytab_cache', records=[('b', 2)], columns=['a', 'b'])
723+
self.assertEqual(res, 'COPY 1')
724+
725+
self.assertIs(
726+
self.con._stmt_cache.get(cache_key, promote=False), entry)
727+
728+
finally:
729+
await self.con.execute('DROP TABLE copytab_cache')
730+
731+
@tb.with_connection_options(statement_cache_size=0)
732+
async def test_copy_records_to_table_no_stmt_cache(self):
733+
await self.con.execute('''
734+
CREATE TABLE copytab_nocache(a text, b int);
735+
''')
736+
737+
try:
738+
for i in range(3):
739+
res = await self.con.copy_records_to_table(
740+
'copytab_nocache', records=[('a', i)])
741+
self.assertEqual(res, 'COPY 1')
742+
743+
# No named statements must be used when the cache is disabled.
744+
self.assertEqual(
745+
await self.con.fetchval(
746+
'SELECT count(*) FROM pg_prepared_statements'),
747+
0)
748+
749+
finally:
750+
await self.con.execute('DROP TABLE copytab_nocache')
751+
703752
async def test_copy_records_to_table_no_binary_codec(self):
704753
await self.con.execute('''
705754
CREATE TABLE copytab(a uuid);

0 commit comments

Comments
 (0)