@@ -211,11 +211,10 @@ func (q Query) ArgDictNode() *pyast.Node {
211211}
212212
213213// 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.
219218func (q Query ) BuildCopyFromBody (isAsync bool ) []* pyast.Node {
220219 var body []* pyast.Node
221220
@@ -238,15 +237,18 @@ func (q Query) BuildCopyFromBody(isAsync bool) []*pyast.Node {
238237 dataVar = argName
239238 }
240239
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
243248 execCall := poet .Node (& pyast.Call {
244- Func : poet .Attribute (poet .Name ("self._conn" ), "execute " ),
249+ Func : poet .Attribute (poet .Name ("self._conn" ), "executemany " ),
245250 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 ,
250252 poet .Name (dataVar ),
251253 },
252254 })
0 commit comments