@@ -211,11 +211,10 @@ func (q Query) ArgDictNode() *pyast.Node {
211
211
}
212
212
213
213
// BuildCopyFromBody generates the method body for :copyfrom commands.
214
- //
215
- // IMPORTANT: This implementation uses executemany for batch inserts, which is
216
- // faster than individual inserts but not as fast as PostgreSQL's native COPY.
217
- // For maximum performance with PostgreSQL, consider using psycopg's copy_from
218
- // directly via conn.connection.driver_connection.
214
+ // This implementation explicitly uses executemany() for batch inserts, which is
215
+ // significantly faster than individual inserts. For PostgreSQL specifically,
216
+ // using the native COPY protocol would be even faster, but executemany provides
217
+ // good performance while maintaining cross-database compatibility.
219
218
func (q Query ) BuildCopyFromBody (isAsync bool ) []* pyast.Node {
220
219
var body []* pyast.Node
221
220
@@ -238,15 +237,18 @@ func (q Query) BuildCopyFromBody(isAsync bool) []*pyast.Node {
238
237
dataVar = argName
239
238
}
240
239
241
- // Build the execute call with the SQL and parameter list
242
- // SQLAlchemy detects the list and uses executemany internally
240
+ // Use executemany explicitly for batch inserts
241
+ // Build the SQL text object first
242
+ sqlText := poet .Node (& pyast.Call {
243
+ Func : poet .Attribute (poet .Name ("sqlalchemy" ), "text" ),
244
+ Args : []* pyast.Node {poet .Name (q .ConstantName )},
245
+ })
246
+
247
+ // Call executemany with the SQL and parameter list
243
248
execCall := poet .Node (& pyast.Call {
244
- Func : poet .Attribute (poet .Name ("self._conn" ), "execute " ),
249
+ Func : poet .Attribute (poet .Name ("self._conn" ), "executemany " ),
245
250
Args : []* pyast.Node {
246
- poet .Node (& pyast.Call {
247
- Func : poet .Attribute (poet .Name ("sqlalchemy" ), "text" ),
248
- Args : []* pyast.Node {poet .Name (q .ConstantName )},
249
- }),
251
+ sqlText ,
250
252
poet .Name (dataVar ),
251
253
},
252
254
})
0 commit comments