Skip to content

Commit 343ecd3

Browse files
committed
add connection argument
1 parent e487619 commit 343ecd3

9 files changed

Lines changed: 311 additions & 24 deletions

File tree

README.md

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -82,20 +82,26 @@ Tooling containing pg2b3dm:
8282

8383
All parameters are optional, except the -t --table option.
8484

85-
If --username and/or --dbname are not specified the current username is used as default.
85+
Preferred database configuration uses `--connection`.
86+
87+
If `--connection` is not specified and none of the deprecated database parameters are used, `pg2b3dm` builds a default connection string using the current username:
88+
`Host=localhost;Port=5432;Username=<current user>;Database=<current user>;CommandTimeOut=0`.
8689

8790
```
88-
-U, --username Database user
91+
-U, --username [Deprecated] Database user. Use --connection instead.
8992
90-
-h, --host (Default: localhost) Database host
93+
-h, --host [Deprecated] (Default: localhost) Database host. Use --connection instead.
9194
92-
-d, --dbname Database name
95+
-d, --dbname [Deprecated] Database name. Use --connection instead.
9396
9497
-c, --column (Default: geom) Geometry column
9598
9699
-t, --table Required. Database table, include database schema if needed
97100
98-
-p, --port (Default: 5432) Database port
101+
-p, --port [Deprecated] (Default: 5432) Database port. Use --connection instead.
102+
103+
--connection (Default: '') Database connection string.
104+
Tip: for long-running queries, include CommandTimeOut=0.
99105
100106
-o, --output (Default: output) Output path
101107
@@ -154,7 +160,7 @@ If --username and/or --dbname are not specified the current username is used as
154160
Sample command for running pg2b3dm:
155161

156162
```
157-
$ pg2b3dm -h localhost -U postgres -c geom_triangle --shaderscolumn shaders -t delaware_buildings -d postgres -g 100,0
163+
$ pg2b3dm --connection "Host=localhost;Username=postgres;Database=postgres;Ssl Mode=Require;CommandTimeOut=0" -c geom_triangle --shaderscolumn shaders -t delaware_buildings -g 100,0
158164
```
159165

160166
Database password will be asked to create the database connection, unless:
@@ -382,7 +388,7 @@ $ docker build -t geodan/pg2b3dm:{name_of_feature_branch} .
382388
Sample on Linux:
383389

384390
```
385-
$ docker run -v $(pwd)/output:/app/output -it geodan/pg2b3dm -h my_host -U my_user -d my_database -t my_schema.my_table
391+
$ docker run -v $(pwd)/output:/app/output -it geodan/pg2b3dm --connection "Host=my_host;Username=my_user;Database=my_database;CommandTimeOut=0" -t my_schema.my_table
386392
```
387393

388394
## Run from source
@@ -398,7 +404,7 @@ To run the app:
398404
```
399405
$ git clone https://github.com/Geodan/pg2b3dm.git
400406
$ cd pg2b3dm/src/pg2b3dm
401-
$ dotnet run -- -h my_host -U my_user -d my_database -t my_schema.my_table
407+
$ dotnet run -- --connection "Host=my_host;Username=my_user;Database=my_database;CommandTimeOut=0" -t my_schema.my_table
402408
```
403409

404410
To create an self-contained executable '~/bin/pg2b3dm' for Linux:

cesium_notes.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ for example visualize a simplified geometry when the camera is far away, and a m
3333
Sample command for using LOD's using the delaware_buildings_lod table (see script 2_create_delaware_table.sql script in the database test project):
3434

3535
```
36-
-h localhost -U postgres -p 5432 -c geom_triangle -t delaware_buildings_lod -d postgres -g 2000 --shaderscolumn shaders --lodcolumn lodcolumn --use_implicit_tiling false -r REPLACE --geometricerrorfactor 8
36+
pg2b3dm --connection "Host=localhost;Port=5432;Username=postgres;Database=postgres;CommandTimeOut=0" -c geom_triangle -t delaware_buildings_lod -g 2000 --shaderscolumn shaders --lodcolumn lodcolumn --use_implicit_tiling false -r REPLACE --geometricerrorfactor 8
3737
```
3838

3939
The LOD function will be enabled when parameter --lodcolumn is not empty.

dataprocessing/dataprocessing_citygml.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ CREATE INDEX ON citydb.geometry_data USING gist(st_centroid(st_envelope(geometry
6262
Once the data is imported, convert the data into 3D Tiles using the `pg2b3dm` tool:
6363

6464
```bash
65-
pg2b3dm -U postgres -p 5440 -d postgres -t citydb.geometry_data -c geometry --attributecolumns geometry_properties
65+
pg2b3dm --connection "Host=localhost;Port=5440;Username=postgres;Database=postgres;CommandTimeOut=0" -t citydb.geometry_data -c geometry --attributecolumns geometry_properties
6666
```
6767

6868
**Result:**
@@ -165,7 +165,7 @@ using pg2b3dm. A recommended filter to add to the view could be "st_geometrytype
165165
Now, generate the 3D Tiles using the new view - adding the shaderscolumn parameter:
166166

167167
```bash
168-
pg2b3dm -U postgres -p 5440 -d postgres -t citydb.geoms4tiles -c geom --shaderscolumn material_data -a objectid,classname
168+
pg2b3dm --connection "Host=localhost;Port=5440;Username=postgres;Database=postgres;CommandTimeOut=0" -t citydb.geoms4tiles -c geom --shaderscolumn material_data -a objectid,classname
169169
```
170170
Result: The exported 3D Tiles will now have different colors based on the CityGML feature classes, enhancing the visualization quality.
171171

getting_started.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ postgresql> CREATE INDEX ON sibbe USING gist(st_centroid(st_envelope(geom)))
5353
- Convert to 3D Tiles using pg2b3dm
5454

5555
```
56-
$ pg2b3dm -h localhost -U postgres -c geom -d postgres -t sibbe -a identificatie
56+
$ pg2b3dm --connection "Host=localhost;Username=postgres;Database=postgres;CommandTimeOut=0" -c geom -t sibbe -a identificatie
5757
```
5858

5959
### Visualize
@@ -76,4 +76,3 @@ viewer.scene.globe.depthTestAgainstTerrain=true;
7676
```
7777

7878
- Load 3D Tiles in Cesium viewer, example result see https://geodan.github.io/pg2b3dm/sample_data/3dbag/sibbe/
79-
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
using Npgsql;
2+
using pg2b3dm;
3+
4+
namespace pg2b3dm.database.tests;
5+
6+
public class ConnectionStringResolverTests
7+
{
8+
[Test]
9+
public void Resolve_UsesExplicitConnectionWithoutAddingCommandTimeout()
10+
{
11+
var connection = "Host=db.example;Username=alice;Database=gis;Ssl Mode=Require;Command Timeout=15";
12+
var options = new pg2b3dm.Options
13+
{
14+
Connection = connection
15+
};
16+
17+
var result = ConnectionStringResolver.Resolve(["--connection", connection], options, "fallback-user");
18+
var builder = new NpgsqlConnectionStringBuilder(result.ConnectionString);
19+
20+
Assert.Multiple(() =>
21+
{
22+
Assert.That(builder.Host, Is.EqualTo("db.example"));
23+
Assert.That(builder.Username, Is.EqualTo("alice"));
24+
Assert.That(builder.Database, Is.EqualTo("gis"));
25+
Assert.That(builder.SslMode, Is.EqualTo(SslMode.Require));
26+
Assert.That(builder.CommandTimeout, Is.EqualTo(15));
27+
Assert.That(result.UserName, Is.EqualTo("alice"));
28+
Assert.That(result.Warnings, Is.Empty);
29+
});
30+
}
31+
32+
[Test]
33+
public void Resolve_LegacyParametersBuildConnectionAndWarn()
34+
{
35+
var options = new pg2b3dm.Options
36+
{
37+
Host = "db.example",
38+
User = "legacy-user",
39+
Database = "legacy-db",
40+
Port = "5433"
41+
};
42+
43+
var result = ConnectionStringResolver.Resolve(["-h", "db.example", "-U", "legacy-user", "-d", "legacy-db", "-p", "5433"], options, "fallback-user");
44+
var builder = new NpgsqlConnectionStringBuilder(result.ConnectionString);
45+
46+
Assert.Multiple(() =>
47+
{
48+
Assert.That(builder.Host, Is.EqualTo("db.example"));
49+
Assert.That(builder.Port, Is.EqualTo(5433));
50+
Assert.That(builder.Username, Is.EqualTo("legacy-user"));
51+
Assert.That(builder.Database, Is.EqualTo("legacy-db"));
52+
Assert.That(builder.CommandTimeout, Is.EqualTo(0));
53+
Assert.That(result.UserName, Is.EqualTo("legacy-user"));
54+
Assert.That(result.Warnings, Has.Count.EqualTo(1));
55+
Assert.That(result.Warnings[0], Does.Contain("--username"));
56+
Assert.That(result.Warnings[0], Does.Contain("--host"));
57+
Assert.That(result.Warnings[0], Does.Contain("--dbname"));
58+
Assert.That(result.Warnings[0], Does.Contain("--port"));
59+
Assert.That(result.Warnings[0], Does.Contain("--connection"));
60+
Assert.That(result.Warnings[0], Does.Contain("CommandTimeOut=0"));
61+
});
62+
}
63+
64+
[Test]
65+
public void Resolve_ConnectionOverridesLegacyParametersAndWarns()
66+
{
67+
var connection = "Host=override-host;Username=preferred-user;Database=preferred-db;Command Timeout=12";
68+
var options = new pg2b3dm.Options
69+
{
70+
Connection = connection,
71+
Host = "ignored-host",
72+
User = "ignored-user",
73+
Database = "ignored-db",
74+
Port = "5433"
75+
};
76+
77+
var result = ConnectionStringResolver.Resolve(["--connection", connection, "--host", "ignored-host", "--username", "ignored-user"], options, "fallback-user");
78+
var builder = new NpgsqlConnectionStringBuilder(result.ConnectionString);
79+
80+
Assert.Multiple(() =>
81+
{
82+
Assert.That(builder.Host, Is.EqualTo("override-host"));
83+
Assert.That(builder.Username, Is.EqualTo("preferred-user"));
84+
Assert.That(builder.Database, Is.EqualTo("preferred-db"));
85+
Assert.That(builder.CommandTimeout, Is.EqualTo(12));
86+
Assert.That(result.UserName, Is.EqualTo("preferred-user"));
87+
Assert.That(result.Warnings, Has.Count.EqualTo(1));
88+
Assert.That(result.Warnings[0], Does.Contain("--connection takes precedence"));
89+
Assert.That(result.Warnings[0], Does.Contain("--host"));
90+
Assert.That(result.Warnings[0], Does.Contain("--username"));
91+
});
92+
}
93+
94+
[Test]
95+
public void Resolve_DefaultsToCurrentUserWhenNoConnectionOptionsAreProvided()
96+
{
97+
var result = ConnectionStringResolver.Resolve(Array.Empty<string>(), new pg2b3dm.Options(), "current-user");
98+
var builder = new NpgsqlConnectionStringBuilder(result.ConnectionString);
99+
100+
Assert.Multiple(() =>
101+
{
102+
Assert.That(builder.Host, Is.EqualTo("localhost"));
103+
Assert.That(builder.Port, Is.EqualTo(5432));
104+
Assert.That(builder.Username, Is.EqualTo("current-user"));
105+
Assert.That(builder.Database, Is.EqualTo("current-user"));
106+
Assert.That(builder.CommandTimeout, Is.EqualTo(0));
107+
Assert.That(result.UserName, Is.EqualTo("current-user"));
108+
Assert.That(result.Warnings, Is.Empty);
109+
});
110+
}
111+
}
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using Npgsql;
5+
6+
namespace pg2b3dm;
7+
8+
public sealed record ConnectionStringResolution(string ConnectionString, string UserName, IReadOnlyList<string> Warnings);
9+
10+
public static class ConnectionStringResolver
11+
{
12+
private const string DefaultHost = "localhost";
13+
private const string DefaultPort = "5432";
14+
private const string ExampleCommand = "pg2b3dm --connection \"Host=localhost;Username=postgres;Database=postgres;Ssl Mode=Require;CommandTimeOut=0\" -t my_schema.my_table";
15+
private static readonly string[] DeprecatedParameterOrder = ["--username", "--host", "--dbname", "--port"];
16+
private static readonly Dictionary<string, string> DeprecatedParameterAliases = new(StringComparer.OrdinalIgnoreCase)
17+
{
18+
["-U"] = "--username",
19+
["--username"] = "--username",
20+
["-h"] = "--host",
21+
["--host"] = "--host",
22+
["-d"] = "--dbname",
23+
["--dbname"] = "--dbname",
24+
["-p"] = "--port",
25+
["--port"] = "--port",
26+
};
27+
28+
public static ConnectionStringResolution Resolve(string[] args, Options options, string currentUserName)
29+
{
30+
var deprecatedParameters = GetDeprecatedParameters(args);
31+
var warnings = new List<string>();
32+
string connectionString;
33+
string userName;
34+
35+
if (!string.IsNullOrWhiteSpace(options.Connection))
36+
{
37+
var builder = new NpgsqlConnectionStringBuilder(options.Connection);
38+
connectionString = builder.ConnectionString;
39+
userName = string.IsNullOrWhiteSpace(builder.Username) ? currentUserName : builder.Username;
40+
41+
if (deprecatedParameters.Count > 0)
42+
{
43+
warnings.Add(CreateConnectionOverrideWarning(deprecatedParameters));
44+
}
45+
}
46+
else
47+
{
48+
var host = string.IsNullOrWhiteSpace(options.Host) ? DefaultHost : options.Host;
49+
var user = string.IsNullOrWhiteSpace(options.User) ? currentUserName : options.User;
50+
var database = string.IsNullOrWhiteSpace(options.Database) ? currentUserName : options.Database;
51+
var port = string.IsNullOrWhiteSpace(options.Port) ? DefaultPort : options.Port;
52+
53+
connectionString = BuildLegacyConnectionString(host, user, database, port);
54+
userName = user;
55+
56+
if (deprecatedParameters.Count > 0)
57+
{
58+
warnings.Add(CreateDeprecatedParametersWarning(deprecatedParameters));
59+
}
60+
}
61+
62+
return new ConnectionStringResolution(connectionString, userName, warnings);
63+
}
64+
65+
public static string AddPassword(string connectionString, string password)
66+
{
67+
var builder = new NpgsqlConnectionStringBuilder(connectionString)
68+
{
69+
Password = password
70+
};
71+
72+
return builder.ConnectionString;
73+
}
74+
75+
public static IReadOnlyList<string> GetDeprecatedParameters(string[] args)
76+
{
77+
var usedParameters = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
78+
79+
foreach (var arg in args)
80+
{
81+
if (TryGetDeprecatedParameter(arg, out var deprecatedParameter))
82+
{
83+
usedParameters.Add(deprecatedParameter);
84+
}
85+
}
86+
87+
return DeprecatedParameterOrder
88+
.Where(usedParameters.Contains)
89+
.ToList();
90+
}
91+
92+
private static bool TryGetDeprecatedParameter(string arg, out string deprecatedParameter)
93+
{
94+
deprecatedParameter = string.Empty;
95+
96+
if (DeprecatedParameterAliases.TryGetValue(arg, out var matchedParameter))
97+
{
98+
deprecatedParameter = matchedParameter;
99+
return true;
100+
}
101+
102+
var equalsIndex = arg.IndexOf('=');
103+
if (equalsIndex > 0)
104+
{
105+
var parameterName = arg[..equalsIndex];
106+
if (DeprecatedParameterAliases.TryGetValue(parameterName, out matchedParameter))
107+
{
108+
deprecatedParameter = matchedParameter;
109+
return true;
110+
}
111+
}
112+
113+
if (arg.StartsWith('-') && !arg.StartsWith("--") && arg.Length > 2)
114+
{
115+
var shortParameterName = arg[..2];
116+
if (DeprecatedParameterAliases.TryGetValue(shortParameterName, out matchedParameter))
117+
{
118+
deprecatedParameter = matchedParameter;
119+
return true;
120+
}
121+
}
122+
123+
return false;
124+
}
125+
126+
private static string BuildLegacyConnectionString(string host, string user, string database, string port)
127+
{
128+
var builder = new NpgsqlConnectionStringBuilder
129+
{
130+
ConnectionString = $"Host={host};Username={user};Database={database};Port={port};CommandTimeOut=0"
131+
};
132+
133+
return builder.ConnectionString;
134+
}
135+
136+
private static string CreateDeprecatedParametersWarning(IReadOnlyList<string> deprecatedParameters)
137+
{
138+
var parameterLabel = deprecatedParameters.Count == 1 ? "parameter" : "parameters";
139+
var verb = deprecatedParameters.Count == 1 ? "is" : "are";
140+
var parameterList = string.Join(", ", deprecatedParameters);
141+
142+
return string.Join(Environment.NewLine, new[]
143+
{
144+
"-----------------------------------------------------------------------------",
145+
$"WARNING: Database {parameterLabel} {parameterList} {verb} deprecated and will be removed in a future version.",
146+
"Use --connection instead.",
147+
$"Example: {ExampleCommand}",
148+
"-----------------------------------------------------------------------------"
149+
});
150+
}
151+
152+
private static string CreateConnectionOverrideWarning(IReadOnlyList<string> deprecatedParameters)
153+
{
154+
var parameterLabel = deprecatedParameters.Count == 1 ? "parameter" : "parameters";
155+
var verb = deprecatedParameters.Count == 1 ? "was" : "were";
156+
var parameterList = string.Join(", ", deprecatedParameters);
157+
158+
return string.Join(Environment.NewLine, new[]
159+
{
160+
"-----------------------------------------------------------------------------",
161+
$"WARNING: Deprecated database {parameterLabel} {parameterList} {verb} provided together with --connection.",
162+
"--connection takes precedence and will be used.",
163+
$"Example: {ExampleCommand}",
164+
"-----------------------------------------------------------------------------"
165+
});
166+
}
167+
}

0 commit comments

Comments
 (0)