@@ -406,6 +406,45 @@ func stripLeadingComments(query string) string {
406406 }
407407}
408408
409+ // queryReturnsResults checks if a SQL query returns a result set.
410+ // This is used to determine whether to send RowDescription or NoData.
411+ func queryReturnsResults (query string ) bool {
412+ upper := strings .ToUpper (stripLeadingComments (query ))
413+ // SELECT is the most common
414+ if strings .HasPrefix (upper , "SELECT" ) {
415+ return true
416+ }
417+ // WITH ... SELECT (CTEs)
418+ if strings .HasPrefix (upper , "WITH" ) {
419+ return true
420+ }
421+ // VALUES clause returns rows
422+ if strings .HasPrefix (upper , "VALUES" ) {
423+ return true
424+ }
425+ // SHOW commands return results
426+ if strings .HasPrefix (upper , "SHOW" ) {
427+ return true
428+ }
429+ // TABLE is shorthand for SELECT * FROM table
430+ if strings .HasPrefix (upper , "TABLE" ) {
431+ return true
432+ }
433+ // EXECUTE can return results if the prepared statement is a SELECT
434+ if strings .HasPrefix (upper , "EXECUTE" ) {
435+ return true
436+ }
437+ // EXPLAIN returns results
438+ if strings .HasPrefix (upper , "EXPLAIN" ) {
439+ return true
440+ }
441+ // DESCRIBE returns results (DuckDB-specific)
442+ if strings .HasPrefix (upper , "DESCRIBE" ) {
443+ return true
444+ }
445+ return false
446+ }
447+
409448func (c * clientConn ) getCommandType (upperQuery string ) string {
410449 // Strip leading comments like /*Fivetran*/ before checking command type
411450 upperQuery = stripLeadingComments (upperQuery )
@@ -958,15 +997,18 @@ func (c *clientConn) handleParse(body []byte) {
958997 }
959998 }
960999
1000+ // Rewrite pg_catalog function calls for compatibility (same as simple query protocol)
1001+ rewrittenQuery := rewritePgCatalogQuery (query )
1002+
9611003 // Convert PostgreSQL $1, $2 placeholders to ? for database/sql
962- convertedQuery , numParams := convertPlaceholders (query )
1004+ convertedQuery , numParams := convertPlaceholders (rewrittenQuery )
9631005
9641006 // Close existing statement with same name
9651007 delete (c .stmts , stmtName )
9661008
9671009 c .stmts [stmtName ] = & preparedStmt {
968- query : query ,
969- convertedQuery : convertedQuery ,
1010+ query : query , // Keep original for logging and Describe
1011+ convertedQuery : convertedQuery , // Rewritten and placeholder-converted for execution
9701012 paramTypes : paramTypes ,
9711013 numParams : numParams ,
9721014 }
@@ -1106,10 +1148,9 @@ func (c *clientConn) handleDescribe(body []byte) {
11061148 }
11071149 c .sendParameterDescription (paramTypes )
11081150
1109- // For SELECT queries, we need to send RowDescription
1151+ // For queries that return results , we need to send RowDescription
11101152 // For other queries, send NoData
1111- upperQuery := strings .ToUpper (strings .TrimSpace (ps .query ))
1112- if ! strings .HasPrefix (upperQuery , "SELECT" ) {
1153+ if ! queryReturnsResults (ps .query ) {
11131154 writeNoData (c .writer )
11141155 return
11151156 }
@@ -1157,9 +1198,8 @@ func (c *clientConn) handleDescribe(body []byte) {
11571198 return
11581199 }
11591200
1160- // For non-SELECT, send NoData
1161- upperQuery := strings .ToUpper (strings .TrimSpace (p .stmt .query ))
1162- if ! strings .HasPrefix (upperQuery , "SELECT" ) {
1201+ // For queries that don't return results, send NoData
1202+ if ! queryReturnsResults (p .stmt .query ) {
11631203 writeNoData (c .writer )
11641204 return
11651205 }
@@ -1239,10 +1279,11 @@ func (c *clientConn) handleExecute(body []byte) {
12391279
12401280 upperQuery := strings .ToUpper (strings .TrimSpace (p .stmt .query ))
12411281 cmdType := c .getCommandType (upperQuery )
1282+ returnsResults := queryReturnsResults (p .stmt .query )
12421283
12431284 log .Printf ("[%s] Execute %q with %d params: %s" , c .username , portalName , len (args ), p .stmt .query )
12441285
1245- if cmdType != "SELECT" {
1286+ if ! returnsResults {
12461287 // Handle nested BEGIN: PostgreSQL issues a warning but continues,
12471288 // while DuckDB throws an error. Match PostgreSQL behavior.
12481289 if cmdType == "BEGIN" && c .txStatus == txStatusTransaction {
@@ -1251,7 +1292,7 @@ func (c *clientConn) handleExecute(body []byte) {
12511292 return
12521293 }
12531294
1254- // Non-SELECT : use Exec with converted query
1295+ // Non-result-returning query : use Exec with converted query
12551296 result , err := c .db .Exec (p .stmt .convertedQuery , args ... )
12561297 if err != nil {
12571298 c .sendError ("ERROR" , "42000" , err .Error ())
@@ -1264,7 +1305,7 @@ func (c *clientConn) handleExecute(body []byte) {
12641305 return
12651306 }
12661307
1267- // SELECT : use Query with converted query
1308+ // Result-returning query : use Query with converted query
12681309 rows , err := c .db .Query (p .stmt .convertedQuery , args ... )
12691310 if err != nil {
12701311 c .sendError ("ERROR" , "42000" , err .Error ())
0 commit comments