diff --git a/.gitignore b/.gitignore index 48bc38ed..9cfb411f 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ spec/.run_auth_specs .cert .envrc result +.direnv diff --git a/spec/pg/connection_spec.cr b/spec/pg/connection_spec.cr index f54f67d0..cf089d05 100644 --- a/spec/pg/connection_spec.cr +++ b/spec/pg/connection_spec.cr @@ -6,6 +6,15 @@ describe PG::Connection, "#initialize" do DB.open("postgres://localhost:5433") } end + + it "set application name" do + URI.parse(DB_URL).tap do |uri| + uri.query = "application_name=specs" + DB.open(uri.to_s) do |db| + db.query_one("SHOW application_name", as: String).should eq("specs") + end + end + end end describe PG::Connection, "#on_notice" do diff --git a/spec/pq/conninfo_spec.cr b/spec/pq/conninfo_spec.cr index 01ff4d95..5c063da8 100644 --- a/spec/pq/conninfo_spec.cr +++ b/spec/pq/conninfo_spec.cr @@ -9,6 +9,7 @@ private def assert_default_params(ci) ci.password.should eq(nil) ci.port.should eq(5432) ci.sslmode.should eq(:prefer) + ci.application_name.should eq("crystal") end private def assert_custom_params(ci) @@ -18,6 +19,7 @@ private def assert_custom_params(ci) ci.password.should eq("pass") ci.port.should eq(5555) ci.sslmode.should eq(:require) + ci.application_name.should eq("specs") end private def assert_ssl_params(ci) @@ -36,7 +38,7 @@ describe PQ::ConnInfo, "parts" do end it "can take settings" do - ci = PQ::ConnInfo.new("host", "db", "user", "pass", 5555, :require) + ci = PQ::ConnInfo.new("host", "db", "user", "pass", 5555, :require, "specs") assert_custom_params ci end end @@ -51,7 +53,7 @@ describe PQ::ConnInfo, ".from_conninfo_string" do it "parses postgres urls" do ci = PQ::ConnInfo.from_conninfo_string( - "postgres://user:pass@host:5555/db?sslmode=require&otherparam=ignore") + "postgres://user:pass@host:5555/db?sslmode=require&otherparam=ignore&application_name=specs") assert_custom_params ci ci = PQ::ConnInfo.from_conninfo_string( @@ -59,7 +61,7 @@ describe PQ::ConnInfo, ".from_conninfo_string" do assert_ssl_params ci ci = PQ::ConnInfo.from_conninfo_string( - "postgresql://user:pass@host:5555/db?sslmode=require") + "postgresql://user:pass@host:5555/db?sslmode=require&application_name=specs") assert_custom_params ci end @@ -72,7 +74,7 @@ describe PQ::ConnInfo, ".from_conninfo_string" do it "parses libpq style strings" do ci = PQ::ConnInfo.from_conninfo_string( - "host=host dbname=db user=user password=pass port=5555 sslmode=require") + "host=host dbname=db user=user password=pass port=5555 sslmode=require application_name=specs") assert_custom_params ci ci = PQ::ConnInfo.from_conninfo_string( @@ -125,12 +127,14 @@ describe PQ::ConnInfo, ".from_conninfo_string" do ENV["PGPASSWORD"] = "C" ENV["PGPORT"] = "1" ENV["PGUSER"] = "D" + ENV["PGAPPNAME"] = "S" ci = PQ::ConnInfo.from_conninfo_string("postgres://") ci.database.should eq("A") ci.host.should eq("B") ci.password.should eq("C") ci.port.should eq(1) ci.user.should eq("D") + ci.application_name.should eq("S") end env_var_bubble do diff --git a/src/pq/connection.cr b/src/pq/connection.cr index 86ef2819..47a45b56 100644 --- a/src/pq/connection.cr +++ b/src/pq/connection.cr @@ -244,7 +244,7 @@ module PQ startup_args = [ "user", @conninfo.user, "database", @conninfo.database, - "application_name", "crystal", + "application_name", @conninfo.application_name, "client_encoding", "utf8", ] diff --git a/src/pq/conninfo.cr b/src/pq/conninfo.cr index 8c4c02c7..728363dc 100644 --- a/src/pq/conninfo.cr +++ b/src/pq/conninfo.cr @@ -35,10 +35,13 @@ module PQ # The sslrootcert. Optional. getter sslrootcert : String? + # The application name. Optional (defaults to "crystal"). + getter application_name : String + getter auth_methods : Array(String) = %w[scram-sha-256-plus scram-sha-256 md5] # Create a new ConnInfo from all parts - def initialize(host : String? = nil, database : String? = nil, user : String? = nil, password : String? = nil, port : Int | String? = nil, sslmode : String | Symbol? = nil) + def initialize(host : String? = nil, database : String? = nil, user : String? = nil, password : String? = nil, port : Int | String? = nil, sslmode : String | Symbol? = nil, application_name : String? = nil) @host = default_host host db = default_database database @database = db.lchop('/') @@ -46,6 +49,7 @@ module PQ @port = (port || ENV.fetch("PGPORT", "5432")).to_i @sslmode = default_sslmode sslmode @password = password || ENV.fetch("PGPASSWORD", PgPass.locate(@host, @port, @database, @user)) + @application_name = default_application_name application_name end # Initialize with either "postgres://" urls or postgres "key=value" pairs @@ -71,8 +75,9 @@ module PQ # Initializes with a `URI` def initialize(uri : URI) - hostname = uri.hostname.presence || URI::Params.parse(uri.query.to_s).fetch("host", "") - initialize(hostname, uri.path, uri.user, uri.password, uri.port, :prefer) + params = URI::Params.parse(uri.query.to_s) + hostname = uri.hostname.presence || params.fetch("host", "") + initialize(hostname, uri.path, uri.user, uri.password, uri.port, :prefer, params.fetch("application_name", nil)) if q = uri.query HTTP::Params.parse(q) do |key, value| handle_sslparam(key, value) @@ -83,10 +88,11 @@ module PQ # Initialize with a `Hash` # # Valid keys match Postgres "conninfo" keys and are `"host"`, `"dbname"`, - # `"user"`, `"password"`, `"port"`, `"sslmode"`, `"sslcert"`, `"sslkey"` and `"sslrootcert"` + # `"user"`, `"password"`, `"port"`, `"sslmode"`, `"sslcert"`, `"sslkey"`, + # `"sslrootcert"` and `"application_name"`. def initialize(params : Hash) initialize(params["host"]?, params["dbname"]?, params["user"]?, - params["password"]?, params["port"]?, params["sslmode"]?) + params["password"]?, params["port"]?, params["sslmode"]?, params["application_name"]?) params.each do |key, value| handle_sslparam(key, value) end @@ -137,6 +143,10 @@ module PQ end end + private def default_application_name(application_name, fallback_application_name = "crystal") + application_name || ENV.fetch("PGAPPNAME", nil) || fallback_application_name + end + private def default_user(u) u || ENV.fetch("PGUSER", current_user_name) end