Skip to content

Commit d1b440a

Browse files
authored
Merge pull request #1753 from lonvia/harden-replication-script
Improvements to osm2pgsql-replication script
2 parents 797ceaa + 2a878f8 commit d1b440a

File tree

2 files changed

+59
-37
lines changed

2 files changed

+59
-37
lines changed

CMakeLists.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,8 +332,11 @@ add_subdirectory(docs)
332332
# Install
333333
#############################################################
334334

335+
include(GNUInstallDirs)
336+
335337
if (ENABLE_INSTALL)
336338
install(TARGETS osm2pgsql DESTINATION bin)
337-
install(PROGRAMS scripts/osm2pgsql-replication DESTINATION bin)
338339
install(FILES default.style empty.style DESTINATION share/osm2pgsql)
340+
install(CODE "set(OSM2PGSQL_BINDIR ${CMAKE_INSTALL_FULL_BINDIR})
341+
configure_file(${PROJECT_SOURCE_DIR}/scripts/osm2pgsql-replication ${CMAKE_INSTALL_FULL_BINDIR}/osm2pgsql-replication)")
339342
endif()

scripts/osm2pgsql-replication

Lines changed: 55 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,11 @@ missing_modules = []
3434

3535
try:
3636
import psycopg2 as psycopg
37+
from psycopg2 import sql
3738
except ImportError:
3839
try:
3940
import psycopg
41+
from psycopg import sql
4042
except ImportError:
4143
missing_modules.append('psycopg2')
4244

@@ -49,6 +51,10 @@ except ImportError:
4951

5052
LOG = logging.getLogger()
5153

54+
# Will be replaced when installed via CMake.
55+
INSTALL_PREFIX = '@OSM2PGSQL_BINDIR@/'
56+
if INSTALL_PREFIX.startswith('@'):
57+
INSTALL_PREFIX = ''
5258

5359
def pretty_format_timedelta(seconds):
5460
minutes = int(seconds/60)
@@ -76,21 +82,28 @@ def connect(args):
7682
""" Create a connection from the given command line arguments.
7783
"""
7884
# If dbname looks like a conninfo string use it as such
79-
if any(part in args.database for part in ['=', '://']):
85+
if args.database and any(part in args.database for part in ['=', '://']):
8086
return psycopg.connect(args.database)
8187

8288
return psycopg.connect(dbname=args.database, user=args.username,
8389
host=args.host, port=args.port)
8490

8591

92+
def table_exists(conn, table_name):
93+
with conn.cursor() as cur:
94+
cur.execute('SELECT * FROM pg_tables where tablename = %s', (table_name, ))
95+
return cur.rowcount > 0
96+
97+
8698
def compute_database_date(conn, prefix):
8799
""" Determine the date of the database from the newest object in the
88100
database.
89101
"""
90102
# First, find the way with the highest ID in the database
91103
# Using nodes would be more reliable but those are not cached by osm2pgsql.
92104
with conn.cursor() as cur:
93-
cur.execute("SELECT max(id) FROM {}_ways".format(prefix))
105+
table = sql.Identifier(f'{prefix}_ways')
106+
cur.execute(sql.SQL("SELECT max(id) FROM {}").format(table))
94107
osmid = cur.fetchone()[0] if cur.rowcount == 1 else None
95108

96109
if osmid is None:
@@ -127,13 +140,13 @@ def setup_replication_state(conn, table, base_url, seq, date):
127140
the given state.
128141
"""
129142
with conn.cursor() as cur:
130-
cur.execute('DROP TABLE IF EXISTS "{}"'.format(table))
131-
cur.execute("""CREATE TABLE "{}"
132-
(url TEXT,
133-
sequence INTEGER,
134-
importdate TIMESTAMP WITH TIME ZONE)
135-
""".format(table))
136-
cur.execute('INSERT INTO "{}" VALUES(%s, %s, %s)'.format(table),
143+
cur.execute(sql.SQL('DROP TABLE IF EXISTS {}').format(table))
144+
cur.execute(sql.SQL("""CREATE TABLE {}
145+
(url TEXT,
146+
sequence INTEGER,
147+
importdate TIMESTAMP WITH TIME ZONE)
148+
""").format(table))
149+
cur.execute(sql.SQL('INSERT INTO {} VALUES(%s, %s, %s)').format(table),
137150
(base_url, seq, date))
138151
conn.commit()
139152

@@ -144,10 +157,10 @@ def update_replication_state(conn, table, seq, date):
144157
"""
145158
with conn.cursor() as cur:
146159
if date is not None:
147-
cur.execute('UPDATE "{}" SET sequence=%s, importdate=%s'.format(table),
160+
cur.execute(sql.SQL('UPDATE {} SET sequence=%s, importdate=%s').format(table),
148161
(seq, date))
149162
else:
150-
cur.execute('UPDATE "{}" SET sequence=%s'.format(table),
163+
cur.execute(sql.SQL('UPDATE {} SET sequence=%s').format(table),
151164
(seq,))
152165

153166
conn.commit()
@@ -196,13 +209,12 @@ def status(conn, args):
196209

197210
results = {}
198211

199-
with conn.cursor() as cur:
200-
cur.execute('SELECT * FROM pg_tables where tablename = %s', (args.table, ))
201-
if cur.rowcount < 1:
202-
results['status'] = 1
203-
results['error'] = "Cannot find replication status table. Run 'osm2pgsql-replication init' first."
204-
else:
205-
cur.execute('SELECT * FROM "{}"'.format(args.table))
212+
if not table_exists(conn, args.table_name):
213+
results['status'] = 1
214+
results['error'] = "Cannot find replication status table. Run 'osm2pgsql-replication init' first."
215+
else:
216+
with conn.cursor() as cur:
217+
cur.execute(sql.SQL('SELECT * FROM {}').format(args.table))
206218
if cur.rowcount != 1:
207219
results['status'] = 2
208220
results['error'] = "Updates not set up correctly. Run 'osm2pgsql-updates init' first."
@@ -341,14 +353,13 @@ def update(conn, args):
341353
may be missing in the rare case that the replication service stops responding
342354
after the updates have been downloaded.
343355
"""
344-
with conn.cursor() as cur:
345-
cur.execute('SELECT * FROM pg_tables where tablename = %s', (args.table, ))
346-
if cur.rowcount < 1:
347-
LOG.fatal("Cannot find replication status table. "
348-
"Run 'osm2pgsql-replication init' first.")
349-
return 1
356+
if not table_exists(conn, args.table_name):
357+
LOG.fatal("Cannot find replication status table. "
358+
"Run 'osm2pgsql-replication init' first.")
359+
return 1
350360

351-
cur.execute('SELECT * FROM "{}"'.format(args.table))
361+
with conn.cursor() as cur:
362+
cur.execute(sql.SQL('SELECT * FROM {}').format(args.table))
352363
if cur.rowcount != 1:
353364
LOG.fatal("Updates not set up correctly. Run 'osm2pgsql-updates init' first.")
354365
return 1
@@ -435,7 +446,12 @@ def get_parser():
435446
help='Print only error messages')
436447
group.add_argument('-v', '--verbose', action='count', default=2,
437448
help='Increase verboseness of output')
438-
group = default_args.add_argument_group('Database arguments')
449+
group = default_args.add_argument_group('Database arguments',
450+
"The following arguments can be used to set the connection parameters to the\n"
451+
"osm2pgsql database. You may also use libpq environment variables to set\n"
452+
"connection parameters, see https://www.postgresql.org/docs/current/libpq-envars.html.\n"
453+
"If your database connection requires a password, use a pgpass file,\n"
454+
"see https://www.postgresql.org/docs/current/libpq-pgpass.html.")
439455
group.add_argument('-d', '--database', metavar='DB',
440456
help='Name of PostgreSQL database to connect to or conninfo string')
441457
group.add_argument('-U', '--username', metavar='NAME',
@@ -477,8 +493,8 @@ def get_parser():
477493
help='File to save changes before they are applied to osm2pgsql.')
478494
cmd.add_argument('--max-diff-size', type=int, default=500,
479495
help='Maximum data to load in MB (default: 500MB)')
480-
cmd.add_argument('--osm2pgsql-cmd', default='osm2pgsql',
481-
help='Path to osm2pgsql command (default: osm2pgsql)')
496+
cmd.add_argument('--osm2pgsql-cmd', default=INSTALL_PREFIX + 'osm2pgsql',
497+
help=f'Path to osm2pgsql command (default: {INSTALL_PREFIX}osm2pgsql)')
482498
cmd.add_argument('--once', action='store_true',
483499
help='Run updates only once, even when more data is available.')
484500
cmd.add_argument('--post-processing', metavar='SCRIPT',
@@ -518,17 +534,20 @@ def main():
518534
datefmt='%Y-%m-%d %H:%M:%S',
519535
level=max(4 - args.verbose, 1) * 10)
520536

521-
if '"' in args.prefix:
522-
LOG.fatal("Prefix must not contain quotation marks.")
523-
return 1
524-
525-
args.table = '{}_replication_status'.format(args.prefix)
537+
args.table_name = f'{args.prefix}_replication_status'
538+
args.table = sql.Identifier(args.table_name)
526539

527540
conn = connect(args)
528-
ret = args.handler(conn, args)
529-
conn.close()
530541

531-
return ret
542+
try:
543+
if not table_exists(conn, f'{args.prefix}_ways'):
544+
LOG.fatal(f'osm2pgsql middle table "{args.prefix}_ways" not found in database "{args.database}". '
545+
'Database needs to be imported in --slim mode.')
546+
return 1
547+
548+
return args.handler(conn, args)
549+
finally:
550+
conn.close()
532551

533552

534553
if __name__ == '__main__':

0 commit comments

Comments
 (0)