session_settings.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. package executor
  2. import (
  3. "strings"
  4. "github.com/danfragoso/pizzasql-next/pkg/parser"
  5. "github.com/danfragoso/pizzasql-next/pkg/pgerror"
  6. "github.com/danfragoso/pizzasql-next/pkg/version"
  7. )
  8. // Session settings.
  9. //
  10. // Drivers and ORMs open a connection with SET statements for parameters that
  11. // have no PizzaSQL equivalent (client_encoding, extra_float_digits,
  12. // statement_timeout, ...). Rejecting them makes an unmodified client unusable,
  13. // so the engine acknowledges every SET and retains only search_path, which
  14. // current_schema() and SHOW report. The engine has no other configurable
  15. // settings; SHOW returns honest constants for the values it exposes.
  16. // normalizeSettingName lowercases a setting name and collapses whitespace so
  17. // SET TIME ZONE and SHOW time zone agree.
  18. func normalizeSettingName(name string) string {
  19. return strings.ToLower(strings.Join(strings.Fields(name), " "))
  20. }
  21. // executeSet acknowledges a session setting.
  22. func (e *Executor) executeSet(stmt *parser.SetStmt) (*Result, error) {
  23. name := normalizeSettingName(stmt.Name)
  24. if name == "search_path" {
  25. var entries []string
  26. for _, value := range stmt.Values {
  27. for _, part := range strings.Split(value, ",") {
  28. part = strings.Trim(strings.TrimSpace(part), `"`)
  29. if part != "" {
  30. entries = append(entries, part)
  31. }
  32. }
  33. }
  34. // Only the emulated schemas exist; drop anything else so SHOW and
  35. // current_schema() describe a schema the engine can actually resolve.
  36. known := make([]string, 0, len(entries))
  37. for _, entry := range entries {
  38. switch strings.ToLower(entry) {
  39. case publicSchemaName, pgCatalogSchemaName, informationSchemaName:
  40. known = append(known, strings.ToLower(entry))
  41. }
  42. }
  43. if len(known) == 0 {
  44. known = []string{publicSchemaName}
  45. }
  46. e.searchPath = known
  47. // A different search path changes which schema is visible; drop the
  48. // statement-scoped catalog snapshot so the next read re-evaluates it.
  49. e.pgCatalog = nil
  50. }
  51. return NewResult("SET"), nil
  52. }
  53. // executeShow returns the value of a setting. Values that the engine does not
  54. // model are reported as constants that describe the actual behavior, and
  55. // unknown names fail rather than inventing a value.
  56. func (e *Executor) executeShow(stmt *parser.ShowStmt) (*Result, error) {
  57. name := normalizeSettingName(stmt.Name)
  58. value, err := e.settingValue(name)
  59. if err != nil {
  60. return nil, err
  61. }
  62. result := NewResult("SHOW")
  63. result.AddColumnWithType(name, "TEXT")
  64. result.AddRow(value)
  65. return result, nil
  66. }
  67. // settingValue reports the value of a setting exposed by SHOW and
  68. // current_setting(). Unknown names are rejected rather than invented.
  69. func (e *Executor) settingValue(name string) (string, error) {
  70. name = normalizeSettingName(name)
  71. var value string
  72. switch name {
  73. case "search_path":
  74. if len(e.searchPath) == 0 {
  75. value = publicSchemaName
  76. } else {
  77. value = strings.Join(e.searchPath, ", ")
  78. }
  79. case "server_version":
  80. value = version.PostgreSQLCompatVersion
  81. case "server_version_num":
  82. // PostgreSQL encodes major*10000 + minor*100.
  83. value = "140000"
  84. case "server_encoding", "client_encoding":
  85. value = "UTF8"
  86. case "transaction_isolation", "default_transaction_isolation":
  87. value = "read committed"
  88. case "standard_conforming_strings", "integer_datetimes":
  89. value = "on"
  90. case "datestyle":
  91. value = "ISO, MDY"
  92. case "timezone", "time zone":
  93. value = "UTC"
  94. case "application_name":
  95. value = ""
  96. case "is_superuser":
  97. value = "off"
  98. default:
  99. return "", pgerror.Unsupported("unrecognized configuration parameter %q", name)
  100. }
  101. return value, nil
  102. }