Skip to content

Commit a0a2594

Browse files
Dev-iLclaude
andcommitted
Add COPY FROM STDIN documentation
Documents binary_copy_to_table and copy_records_to_table on a dedicated docs/components/copy.md page with tabbed Connection/Transaction examples. connection.md and transaction.md reference that page. Sidebar updated. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 87bf681 commit a0a2594

4 files changed

Lines changed: 155 additions & 0 deletions

File tree

docs/.vuepress/sidebar.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export default sidebar({
2222
"connection_pool_builder",
2323
"connection",
2424
"transaction",
25+
"copy",
2526
"cursor",
2627
"prepared_statement",
2728
"listener",

docs/components/connection.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,11 @@ async def main() -> None:
245245
)
246246
```
247247

248+
### COPY FROM STDIN
249+
250+
`Connection` supports bulk-loading via `binary_copy_to_table` and `copy_records_to_table`.
251+
See the [COPY FROM STDIN](./copy.md) page for full documentation and examples.
252+
248253
### Close
249254
Returns connection to the pool.
250255
It's crucial to commit all transactions and close all cursor which are made from the connection.

docs/components/copy.md

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
---
2+
title: COPY FROM STDIN
3+
---
4+
5+
PSQLPy exposes two methods for bulk-loading data via PostgreSQL's `COPY FROM STDIN` protocol.
6+
Both are available on `Connection` and `Transaction`.
7+
8+
## Binary Copy To Table
9+
10+
#### Parameters:
11+
12+
- `source`: bytes, bytearray, or `BytesIO` containing a PostgreSQL binary COPY stream.
13+
- `table_name`: name of the target table.
14+
- `columns`: sequence of column names to load into. When `None`, all table columns are used in their declared order.
15+
- `schema_name`: optional schema for `table_name`.
16+
17+
Stream a pre-encoded PostgreSQL binary COPY payload directly into a table.
18+
Executes `COPY table_name (<columns>) FROM STDIN (FORMAT binary)`.
19+
20+
::: warning
21+
You are responsible for encoding the bytes correctly. Passing an invalid binary COPY stream will result in a database error.
22+
:::
23+
24+
::: tabs
25+
26+
@tab Connection
27+
```python
28+
async def main() -> None:
29+
...
30+
connection = await db_pool.connection()
31+
with open("data.bin", "rb") as f:
32+
inserted = await connection.binary_copy_to_table(
33+
source=f.read(),
34+
table_name="users",
35+
columns=["id", "username"],
36+
)
37+
print(f"Inserted {inserted} rows")
38+
```
39+
40+
@tab Transaction
41+
```python
42+
async def main() -> None:
43+
...
44+
connection = await db_pool.connection()
45+
async with connection.transaction() as transaction:
46+
with open("data.bin", "rb") as f:
47+
inserted = await transaction.binary_copy_to_table(
48+
source=f.read(),
49+
table_name="users",
50+
columns=["id", "username"],
51+
)
52+
print(f"Inserted {inserted} rows")
53+
```
54+
55+
:::
56+
57+
## Copy Records To Table
58+
59+
#### Parameters:
60+
61+
- `table_name`: name of the target table.
62+
- `records`: iterable of records, where each record is a sequence of column values.
63+
- `columns`: sequence of column names to load into. When `None`, all table columns are used in their declared order.
64+
- `schema_name`: optional schema for `table_name`.
65+
66+
Bulk-load plain Python records into a table via the binary `COPY FROM STDIN` protocol.
67+
Column types are introspected from the target table automatically, so each record may contain ordinary Python values — the same types accepted by `execute()`.
68+
Returns the number of inserted rows.
69+
70+
This is the ergonomic alternative to `binary_copy_to_table` when you have Python data rather than a pre-encoded binary stream.
71+
72+
::: tabs
73+
74+
@tab Connection
75+
```python
76+
from datetime import datetime, timezone
77+
78+
async def main() -> None:
79+
...
80+
connection = await db_pool.connection()
81+
records = [
82+
(1, "alpha", 1.5, datetime(2026, 1, 1, tzinfo=timezone.utc)),
83+
(2, "beta", 2.25, datetime(2026, 1, 2, tzinfo=timezone.utc)),
84+
(3, "gamma", None, datetime(2026, 1, 3, tzinfo=timezone.utc)),
85+
]
86+
inserted = await connection.copy_records_to_table(
87+
table_name="measurements",
88+
records=records,
89+
)
90+
print(f"Inserted {inserted} rows")
91+
```
92+
93+
@tab Transaction
94+
```python
95+
from datetime import datetime, timezone
96+
97+
async def main() -> None:
98+
...
99+
connection = await db_pool.connection()
100+
records = [
101+
(1, "alpha", 1.5, datetime(2026, 1, 1, tzinfo=timezone.utc)),
102+
(2, "beta", 2.25, datetime(2026, 1, 2, tzinfo=timezone.utc)),
103+
(3, "gamma", None, datetime(2026, 1, 3, tzinfo=timezone.utc)),
104+
]
105+
async with connection.transaction() as transaction:
106+
inserted = await transaction.copy_records_to_table(
107+
table_name="measurements",
108+
records=records,
109+
)
110+
print(f"Inserted {inserted} rows")
111+
```
112+
113+
:::
114+
115+
You can load only a subset of columns by providing the `columns` argument:
116+
117+
::: tabs
118+
119+
@tab Connection
120+
```python
121+
async def main() -> None:
122+
...
123+
connection = await db_pool.connection()
124+
inserted = await connection.copy_records_to_table(
125+
table_name="measurements",
126+
records=[(1, "alpha"), (2, "beta")],
127+
columns=["id", "label"],
128+
)
129+
```
130+
131+
@tab Transaction
132+
```python
133+
async def main() -> None:
134+
...
135+
connection = await db_pool.connection()
136+
async with connection.transaction() as transaction:
137+
inserted = await transaction.copy_records_to_table(
138+
table_name="measurements",
139+
records=[(1, "alpha"), (2, "beta")],
140+
columns=["id", "label"],
141+
)
142+
```
143+
144+
:::

docs/components/transaction.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,3 +426,8 @@ async def main() -> None:
426426
dict_result: List[Dict[Any, Any]] = fetched_result.result()
427427
... # do something with the result.
428428
```
429+
430+
### COPY FROM STDIN
431+
432+
`Transaction` supports bulk-loading via `binary_copy_to_table` and `copy_records_to_table`.
433+
See the [COPY FROM STDIN](./copy.md) page for full documentation and examples.

0 commit comments

Comments
 (0)