@@ -292,6 +292,16 @@ func (c *clientConn) handleQuery(body []byte) error {
292292
293293 // For non-SELECT queries, use Exec
294294 if cmdType != "SELECT" {
295+ // Handle nested BEGIN: PostgreSQL issues a warning but continues,
296+ // while DuckDB throws an error. Match PostgreSQL behavior.
297+ if cmdType == "BEGIN" && c .txStatus == txStatusTransaction {
298+ c .sendNotice ("WARNING" , "25001" , "there is already a transaction in progress" )
299+ writeCommandComplete (c .writer , "BEGIN" )
300+ writeReadyForQuery (c .writer , c .txStatus )
301+ c .writer .Flush ()
302+ return nil
303+ }
304+
295305 result , err := c .db .Exec (query )
296306 if err != nil {
297307 c .sendError ("ERROR" , "42000" , err .Error ())
@@ -902,6 +912,11 @@ func (c *clientConn) sendError(severity, code, message string) {
902912 c .writer .Flush ()
903913}
904914
915+ func (c * clientConn ) sendNotice (severity , code , message string ) {
916+ writeNoticeResponse (c .writer , severity , code , message )
917+ // Don't flush here - let the caller decide when to flush
918+ }
919+
905920// Extended query protocol handlers
906921
907922func (c * clientConn ) handleParse (body []byte ) {
@@ -1228,6 +1243,14 @@ func (c *clientConn) handleExecute(body []byte) {
12281243 log .Printf ("[%s] Execute %q with %d params: %s" , c .username , portalName , len (args ), p .stmt .query )
12291244
12301245 if cmdType != "SELECT" {
1246+ // Handle nested BEGIN: PostgreSQL issues a warning but continues,
1247+ // while DuckDB throws an error. Match PostgreSQL behavior.
1248+ if cmdType == "BEGIN" && c .txStatus == txStatusTransaction {
1249+ c .sendNotice ("WARNING" , "25001" , "there is already a transaction in progress" )
1250+ writeCommandComplete (c .writer , "BEGIN" )
1251+ return
1252+ }
1253+
12311254 // Non-SELECT: use Exec with converted query
12321255 result , err := c .db .Exec (p .stmt .convertedQuery , args ... )
12331256 if err != nil {
0 commit comments