Skip to content

Commit 004d82e

Browse files
authored
Merge pull request #26 from PostHog/fix/ignore-postgres-set-commands
Ignore PostgreSQL-specific SET commands
2 parents 3af9652 + ae07b0e commit 004d82e

File tree

3 files changed

+189
-0
lines changed

3 files changed

+189
-0
lines changed

server/catalog.go

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,8 +359,140 @@ var (
359359
setApplicationNameToRegex = regexp.MustCompile(`(?i)^SET\s+application_name\s+TO\s+`)
360360
// SHOW application_name -> SELECT getvariable('application_name') AS application_name
361361
showApplicationNameRegex = regexp.MustCompile(`(?i)^SHOW\s+application_name\s*;?\s*$`)
362+
// Regex to extract SET parameter name
363+
setParameterRegex = regexp.MustCompile(`(?i)^SET\s+(?:SESSION\s+|LOCAL\s+)?(\w+)`)
362364
)
363365

366+
// PostgreSQL-specific SET parameters that DuckDB doesn't support.
367+
// These will be silently ignored (return SET success without executing).
368+
var ignoredSetParameters = map[string]bool{
369+
// SSL/Connection settings
370+
"ssl_renegotiation_limit": true,
371+
372+
// Statement/Lock timeouts
373+
"statement_timeout": true,
374+
"lock_timeout": true,
375+
"idle_in_transaction_session_timeout": true,
376+
"idle_session_timeout": true,
377+
378+
// Client connection settings
379+
"client_min_messages": true,
380+
"log_min_messages": true,
381+
"log_min_duration_statement": true,
382+
"log_statement": true,
383+
384+
// Transaction settings (DuckDB handles these differently)
385+
"default_transaction_isolation": true,
386+
"default_transaction_read_only": true,
387+
"default_transaction_deferrable": true,
388+
"transaction_isolation": true,
389+
"transaction_read_only": true,
390+
"transaction_deferrable": true,
391+
392+
// Encoding (DuckDB is always UTF-8)
393+
"client_encoding": true,
394+
395+
// PostgreSQL-specific features
396+
"row_security": true,
397+
"check_function_bodies": true,
398+
"default_tablespace": true,
399+
"temp_tablespaces": true,
400+
"session_replication_role": true,
401+
"vacuum_freeze_min_age": true,
402+
"vacuum_freeze_table_age": true,
403+
"bytea_output": true,
404+
"xmlbinary": true,
405+
"xmloption": true,
406+
"gin_pending_list_limit": true,
407+
"gin_fuzzy_search_limit": true,
408+
409+
// Locale settings
410+
"lc_messages": true,
411+
"lc_monetary": true,
412+
"lc_numeric": true,
413+
"lc_time": true,
414+
415+
// Constraint settings
416+
"constraint_exclusion": true,
417+
"cursor_tuple_fraction": true,
418+
"from_collapse_limit": true,
419+
"join_collapse_limit": true,
420+
"geqo": true,
421+
"geqo_threshold": true,
422+
423+
// Parallel query settings
424+
"max_parallel_workers_per_gather": true,
425+
"max_parallel_workers": true,
426+
"parallel_leader_participation": true,
427+
"parallel_tuple_cost": true,
428+
"parallel_setup_cost": true,
429+
"min_parallel_table_scan_size": true,
430+
"min_parallel_index_scan_size": true,
431+
432+
// Planner settings
433+
"enable_bitmapscan": true,
434+
"enable_hashagg": true,
435+
"enable_hashjoin": true,
436+
"enable_indexscan": true,
437+
"enable_indexonlyscan": true,
438+
"enable_material": true,
439+
"enable_mergejoin": true,
440+
"enable_nestloop": true,
441+
"enable_parallel_append": true,
442+
"enable_parallel_hash": true,
443+
"enable_partition_pruning": true,
444+
"enable_partitionwise_join": true,
445+
"enable_partitionwise_aggregate": true,
446+
"enable_seqscan": true,
447+
"enable_sort": true,
448+
"enable_tidscan": true,
449+
"enable_gathermerge": true,
450+
451+
// Cost settings
452+
"seq_page_cost": true,
453+
"random_page_cost": true,
454+
"cpu_tuple_cost": true,
455+
"cpu_index_tuple_cost": true,
456+
"cpu_operator_cost": true,
457+
"effective_cache_size": true,
458+
459+
// Memory settings
460+
"work_mem": true,
461+
"maintenance_work_mem": true,
462+
"logical_decoding_work_mem": true,
463+
"temp_buffers": true,
464+
465+
// Misc settings that don't apply
466+
"synchronous_commit": true,
467+
"commit_delay": true,
468+
"commit_siblings": true,
469+
"huge_pages": true,
470+
"force_parallel_mode": true,
471+
"jit": true,
472+
"jit_above_cost": true,
473+
"jit_inline_above_cost": true,
474+
"jit_optimize_above_cost": true,
475+
476+
// Replication settings
477+
"synchronous_standby_names": true,
478+
"wal_sender_timeout": true,
479+
"wal_receiver_timeout": true,
480+
481+
// Search path is handled separately but good to have here as fallback
482+
// "search_path": true, // This one we might want to handle specially later
483+
}
484+
485+
// isIgnoredSetParameter checks if a SET command targets a PostgreSQL-specific
486+
// parameter that should be silently ignored in DuckDB.
487+
func isIgnoredSetParameter(query string) bool {
488+
matches := setParameterRegex.FindStringSubmatch(query)
489+
if len(matches) < 2 {
490+
return false
491+
}
492+
paramName := strings.ToLower(matches[1])
493+
return ignoredSetParameters[paramName]
494+
}
495+
364496
// rewritePgCatalogQuery rewrites PostgreSQL-specific syntax for DuckDB compatibility
365497
func rewritePgCatalogQuery(query string) string {
366498
// Replace pg_catalog.func_name( with func_name(

server/conn.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,15 @@ func (c *clientConn) handleQuery(body []byte) error {
280280

281281
log.Printf("[%s] Query: %s", c.username, query)
282282

283+
// Check if this is a PostgreSQL-specific SET command that should be ignored
284+
if isIgnoredSetParameter(query) {
285+
log.Printf("[%s] Ignoring PostgreSQL-specific SET: %s", c.username, query)
286+
writeCommandComplete(c.writer, "SET")
287+
writeReadyForQuery(c.writer, c.txStatus)
288+
c.writer.Flush()
289+
return nil
290+
}
291+
283292
// Rewrite pg_catalog function calls for compatibility
284293
query = rewritePgCatalogQuery(query)
285294

@@ -1305,6 +1314,13 @@ func (c *clientConn) handleExecute(body []byte) {
13051314

13061315
log.Printf("[%s] Execute %q with %d params: %s", c.username, portalName, len(args), p.stmt.query)
13071316

1317+
// Check if this is a PostgreSQL-specific SET command that should be ignored
1318+
if isIgnoredSetParameter(p.stmt.query) {
1319+
log.Printf("[%s] Ignoring PostgreSQL-specific SET: %s", c.username, p.stmt.query)
1320+
writeCommandComplete(c.writer, "SET")
1321+
return
1322+
}
1323+
13081324
if !returnsResults {
13091325
// Handle nested BEGIN: PostgreSQL issues a warning but continues,
13101326
// while DuckDB throws an error. Match PostgreSQL behavior.

server/conn_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,3 +562,44 @@ func TestQueryReturnsResultsWithComments(t *testing.T) {
562562
})
563563
}
564564
}
565+
566+
func TestIsIgnoredSetParameter(t *testing.T) {
567+
tests := []struct {
568+
name string
569+
query string
570+
expected bool
571+
}{
572+
// PostgreSQL-specific SET commands that should be ignored
573+
{"ssl_renegotiation_limit", "SET ssl_renegotiation_limit = 0", true},
574+
{"ssl_renegotiation_limit TO", "SET ssl_renegotiation_limit TO 0", true},
575+
{"statement_timeout", "SET statement_timeout = '30s'", true},
576+
{"lock_timeout", "SET lock_timeout = 1000", true},
577+
{"client_encoding", "SET client_encoding = 'UTF8'", true},
578+
{"client_min_messages", "SET client_min_messages = warning", true},
579+
{"row_security", "SET row_security = on", true},
580+
{"work_mem", "SET work_mem = '64MB'", true},
581+
{"enable_seqscan", "SET enable_seqscan = off", true},
582+
{"jit", "SET jit = off", true},
583+
{"synchronous_commit", "SET synchronous_commit = off", true},
584+
{"SESSION prefix", "SET SESSION statement_timeout = 0", true},
585+
{"LOCAL prefix", "SET LOCAL lock_timeout = 5000", true},
586+
{"case insensitive", "set SSL_RENEGOTIATION_LIMIT = 0", true},
587+
588+
// SET commands that should NOT be ignored (pass through to DuckDB)
589+
{"application_name", "SET application_name = 'myapp'", false},
590+
{"timezone", "SET timezone = 'UTC'", false},
591+
{"search_path", "SET search_path = public", false},
592+
{"random DuckDB setting", "SET threads = 4", false},
593+
{"not a SET command", "SELECT 1", false},
594+
{"SET in string", "SELECT 'SET ssl_renegotiation_limit = 0'", false},
595+
}
596+
597+
for _, tt := range tests {
598+
t.Run(tt.name, func(t *testing.T) {
599+
result := isIgnoredSetParameter(tt.query)
600+
if result != tt.expected {
601+
t.Errorf("isIgnoredSetParameter(%q) = %v, want %v", tt.query, result, tt.expected)
602+
}
603+
})
604+
}
605+
}

0 commit comments

Comments
 (0)