Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1tx1gl-001z2D-T7 for pgsql-general@arkaria.postgresql.org; Tue, 25 Mar 2025 10:39:20 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.94.2) (envelope-from ) id 1tx1gk-0029Oy-Kz for pgsql-general@arkaria.postgresql.org; Tue, 25 Mar 2025 10:39:18 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1tx1gk-0029Oq-5X for pgsql-general@lists.postgresql.org; Tue, 25 Mar 2025 10:39:18 +0000 Received: from mail-ed1-x52c.google.com ([2a00:1450:4864:20::52c]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.96) (envelope-from ) id 1tx1gi-0011bK-1K for pgsql-general@postgresql.org; Tue, 25 Mar 2025 10:39:17 +0000 Received: by mail-ed1-x52c.google.com with SMTP id 4fb4d7f45d1cf-5e6c18e2c7dso9744467a12.3 for ; Tue, 25 Mar 2025 03:39:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1742899155; x=1743503955; darn=postgresql.org; h=to:subject:message-id:date:from:mime-version:from:to:cc:subject :date:message-id:reply-to; bh=O6/i8CyGRkqEC+OVVymTK+ftQ+LwwMJBj7YRLKnllGM=; b=aJa9N6N9vccjqhwL4GHPnByzzvrpYlaESPVXhKf3sgzjIhs9X6l8SRjuGqi9GSQjKr gp5rlYE5LuNXL/OEKBSd2AE+d8ZgYEV94+hQVYd4QSFW1iX2GjvwY99kjke8bwl8XpB2 mm11vWA1n53L5dFsw+oxEVpXZ/m4IIUO/v7/0dd2HVdZRsNP53taifbd7jX/NAdbuIU5 3JkrJFeLYXxkpIN79NbI/KgmsbzYyY36NCQ1BXuEj92PIb8RB2kCHLi50OBzDsJk8Ils OtzO/jAYX5KVf71MvDAruJfM15+DEmuF81JhwvezGDYN4YKiiv2JLi4I0fBa0UnHHFaa Dljw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1742899155; x=1743503955; h=to:subject:message-id:date:from:mime-version:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=O6/i8CyGRkqEC+OVVymTK+ftQ+LwwMJBj7YRLKnllGM=; b=ECf/F9UQ75/0hNRM+9h2Qe8wfzhIkkN8yRLmLIm17SMm3JkWzv5VFBV6Lpk4+j8DLl PwMSm/AEAaLtDONESIwAhRtnQ3e71SD9TTAvZfIvfzYh2mXpu2bl+P1GLeESTL+Vws+C CVSwaoQ56Bk/OOUWQfXQ9LKiXkKWlWMwy5AJCoZrWwdPIakgMizKMIR7V1FLUCX4eA0m UYDZQiB2o1x6JThNCKPuN2ey6xKgrdyJ2vFeu4mZwZMv8TRuiunfgQ9ejO4PHYYASAHV V9VtL1J+vvlid+e0bN8+MvbenzODbssxMOAsFELyKYGPeO8/ZfrL19U3qO+QghsjuPkh 5Iiw== X-Gm-Message-State: AOJu0YygGV5lu2DV8+d2WzwoW5WrXaKKIpvAoV51F5Wk2zO/1qGXdLSb KX4ZB58QAm9PBkDOeqqpe/mTSkgUJqbEJVMUXQnlTTDcIH1y90cOztj0bJkvNraDq7wxxcjwebP h+OThe5Dat3iMc/yXQqsrb23RNAOAo1Ie X-Gm-Gg: ASbGnctli61JpI5kYhM45bFWU5SvumWtk2xO/JOGsClb4mMK3kxwF5u/6qA9ah+i0TM cKKNCJR5pjejB3oeI3hyj3udZ2YL1/9oMshqAD8/Hn5/tbUhLBSjMeuHIIRiBFEcuHlG9pWsLqK wY4WYMT8Bmtr+RB8rKqCCtcQgWA0w= X-Google-Smtp-Source: AGHT+IHCLCeugKTIfL4aIjj1Ziw70gJPE+nLFoATKk2TfMJ1dzQ7ATFYEZrXgFjMehukZ5SqziSImbSaFhbEqZggANc= X-Received: by 2002:a05:6402:5002:b0:5ed:18dc:c0fb with SMTP id 4fb4d7f45d1cf-5ed18dcc81amr1375404a12.0.1742899154569; Tue, 25 Mar 2025 03:39:14 -0700 (PDT) MIME-Version: 1.0 From: Alexander Farber Date: Tue, 25 Mar 2025 11:39:03 +0100 X-Gm-Features: AQ5f1Jro616RfKw-q6MTCaXDMU8AMp02tTpVFoOg50wFid0mcrqpkJZjjfdVLDM Message-ID: Subject: How to pass a list of locations (longitude, latitude) to a PostgreSQL/PostGIS stored function? To: pgsql-general Content-Type: multipart/alternative; boundary="000000000000fcd2040631285481" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --000000000000fcd2040631285481 Content-Type: text/plain; charset="UTF-8" Hello dear PostgreSQL users I have prepared a https://dbfiddle.uk/vOFXNgns for my question and also list my SQL code below. I have created a countires_boundaries table, which I intend to fill with .poly files provided at Geofabrik: CREATE EXTENSION IF NOT EXISTS postgis; CREATE TABLE IF NOT EXISTS countries_boundaries ( country TEXT PRIMARY KEY CHECK (country ~ '^[a-z]{2}$'), boundary GEOMETRY(MULTIPOLYGON, 4326) NOT NULL ); CREATE INDEX IF NOT EXISTS countries_boundaries_index_1 ON countries_boundaries USING GIST (boundary); Then I am trying to add a function, which would receive a series of locations (longitude and latitude pairs in microdegrees) and return a list of lowercase 2-letter country codes, like "de", "pl", "lv": CREATE OR REPLACE FUNCTION find_countries(locations BIGINT[][]) RETURNS TABLE (country TEXT) AS $$ SELECT DISTINCT enclosing_countries.country FROM unnest(locations) AS location_array(lng, lat) JOIN LATERAL ( SELECT country FROM countries_boundaries -- Convert microdegrees to degrees and check if the location lies within the country boundary. WHERE ST_Contains( boundary, ST_SetSRID( ST_MakePoint(lng / 1000000.0, lat / 1000000.0), 4326 ) ) ) AS enclosing_countries ON TRUE; $$ LANGUAGE sql STABLE; Unfortunately, this gives me the error: table "location_array" has 1 columns available but 2 columns specified I have also tried: CREATE OR REPLACE FUNCTION find_countries(locations BIGINT[][]) RETURNS TABLE (country TEXT) AS $$ SELECT DISTINCT enclosing_countries.country FROM unnest(locations) AS location JOIN LATERAL ( SELECT country FROM countries_boundaries -- Convert microdegrees to degrees and check if the location lies within the country boundary. WHERE ST_Contains( boundary, ST_SetSRID( ST_MakePoint(location[1] / 1000000.0, location[2] / 1000000.0), 4326 ) ) ) AS enclosing_countries ON TRUE; $$ LANGUAGE sql STABLE; But that gives me the error: cannot subscript type bigint because it does not support subscripting I had even more attempts at fixing my issue, but have not succeeded yet In the long run I am trying to call the function from an ASP.Net Core 8 app as: public async Task> FindCountries(IEnumerable<(long lng, long lat)> locations) { HashSet countries = []; await retryPolicy.ExecuteAsync(async () => { await using NpgsqlConnection connection = new(connectionString); await connection.OpenAsync(); using NpgsqlCommand command = new("SELECT country FROM find_countries(@locations)", connection); // convert locations into the expected format (array of BIGINT pairs) List<(long lng, long lat)> locationList = [.. locations]; long[][] locationArray = [.. locationList.Select(loc => new long[] { loc.lng, loc.lat })]; command.Parameters.AddWithValue("locations", locationArray); await using NpgsqlDataReader reader = await command.ExecuteReaderAsync(); while (await reader.ReadAsync()) { string countryCode = reader.GetString(0); if (!string.IsNullOrWhiteSpace(countryCode)) { countries.Add(countryCode); } } }); return countries; } Best regards Alex --000000000000fcd2040631285481 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
He= llo dear PostgreSQL users

I have prepared a https://dbfiddle.uk/vOFXNgns=C2=A0 for my question and= also list my SQL code below.

I have created a cou= ntires_boundaries table, which I intend to fill with .poly files provided a= t Geofabrik:

=C2=A0 =C2=A0 CREATE EXTENSION IF NOT= EXISTS postgis;

=C2=A0 =C2=A0 CREATE TABLE IF NOT= EXISTS countries_boundaries (
=C2=A0 =C2=A0 =C2=A0 =C2=A0 countr= y TEXT PRIMARY KEY CHECK (country ~ '^[a-z]{2}$'),
=C2=A0= =C2=A0 =C2=A0 =C2=A0 boundary GEOMETRY(MULTIPOLYGON, 4326) NOT NULL
<= div>=C2=A0 =C2=A0 );

=C2=A0 =C2=A0 CREATE INDEX IF= NOT EXISTS countries_boundaries_index_1
=C2=A0 =C2=A0 ON countri= es_boundaries
=C2=A0 =C2=A0 USING GIST (boundary);

=
Then I am trying to add a function, which would receive a series= of locations (longitude and latitude pairs in microdegrees) and return a l= ist of lowercase 2-letter country codes, like "de", "pl"= ;, "lv":

=C2=A0 =C2=A0 CREATE OR REPLACE= FUNCTION find_countries(locations BIGINT[][])
=C2=A0 =C2=A0 RETU= RNS TABLE (country TEXT) AS $$
=C2=A0 =C2=A0 SELECT DISTINCT encl= osing_countries.country
=C2=A0 =C2=A0 FROM unnest(locations) AS l= ocation_array(lng, lat)
=C2=A0 =C2=A0 JOIN LATERAL (
= =C2=A0 =C2=A0 =C2=A0 =C2=A0 SELECT country
=C2=A0 =C2=A0 =C2=A0 = =C2=A0 FROM countries_boundaries
=C2=A0 =C2=A0 =C2=A0 =C2=A0 -- C= onvert microdegrees to degrees and check if the location lies within the co= untry boundary.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 WHERE ST_Contains(
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 boun= dary,=C2=A0
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 ST_SetSRID(
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ST_MakePoint(lng / 1000000.0, lat / 1000= 000.0),=C2=A0
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 4326
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 )
=C2=A0 =C2=A0 ) AS enclosing_countries ON TRU= E;
=C2=A0 =C2=A0 $$ LANGUAGE sql STABLE;

Unfortunately, this gives me the error:

=C2=A0 = =C2=A0 table "location_array" has 1 columns available but 2 colum= ns specified

I have also tried:

=C2=A0 =C2=A0 CREATE OR REPLACE FUNCTION find_countries(locations BI= GINT[][])
=C2=A0 =C2=A0 RETURNS TABLE (country TEXT) AS $$
<= div>=C2=A0 =C2=A0 SELECT DISTINCT enclosing_countries.country
=C2= =A0 =C2=A0 FROM unnest(locations) AS location
=C2=A0 =C2=A0 JOIN = LATERAL (
=C2=A0 =C2=A0 =C2=A0 =C2=A0 SELECT country
= =C2=A0 =C2=A0 =C2=A0 =C2=A0 FROM countries_boundaries
=C2=A0 =C2= =A0 =C2=A0 =C2=A0 -- Convert microdegrees to degrees and check if the locat= ion lies within the country boundary.
=C2=A0 =C2=A0 =C2=A0 =C2=A0= WHERE ST_Contains(
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 boundary,=C2=A0
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ST_SetSRID(
=C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ST_MakePoint(locati= on[1] / 1000000.0, location[2] / 1000000.0),=C2=A0
=C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 4326
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 )
=C2=A0 =C2= =A0 ) AS enclosing_countries ON TRUE;
=C2=A0 =C2=A0 $$ LANGUAGE s= ql STABLE;

But that gives me the error:
=
=C2=A0 =C2=A0 cannot subscript type bigint because it does n= ot support subscripting

I had even more attempts at fixing my= issue, but have not succeeded yet

In the long run I am trying to ca= ll the function from an ASP.Net Core 8 app as:

=C2=A0 =C2=A0 pu= blic async Task<ISet<string>> FindCountries(IEnumerable<(lon= g lng, long lat)> locations)
=C2=A0 =C2=A0 {
=C2=A0 = =C2=A0 =C2=A0 =C2=A0 HashSet<string> countries =3D [];

=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 await retryPolicy.ExecuteAsync(async= () =3D>
=C2=A0 =C2=A0 =C2=A0 =C2=A0 {
=C2=A0 =C2=A0= =C2=A0 =C2=A0 =C2=A0 =C2=A0 await using NpgsqlConnection connection =3D ne= w(connectionString);
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 aw= ait connection.OpenAsync();
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 using NpgsqlCommand command =3D new("SELECT country FROM find_c= ountries(@locations)", connection);

=C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 // convert locations into the expected f= ormat (array of BIGINT pairs)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 List<(long lng, long lat)> locationList =3D [.. locations];
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 long[][] locationArray = =3D [.. locationList.Select(loc =3D> new long[] { loc.lng, loc.lat })];<= /div>
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 command.Parameters.AddW= ithValue("locations", locationArray);

= =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 await using NpgsqlDataReader read= er =3D await command.ExecuteReaderAsync();
=C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 while (await reader.ReadAsync())
=C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 string countryCode =3D reader.GetString(0);
<= div>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (!string.IsN= ullOrWhiteSpace(countryCode))
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 countries.Add(countryCode);
=C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
=C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
=C2=A0 =C2=A0 =C2=A0 =C2=A0 });

=C2=A0 =C2=A0 =C2=A0 =C2=A0 return countries;
=C2=A0 = =C2=A0 }

Best regards
Alex
--000000000000fcd2040631285481--