Skip to content

Commit 44c5d2d

Browse files
committed
SQLite: use primary keys and "INSERT OR REPLACE"
Also, split code into functions
1 parent 7bf9864 commit 44c5d2d

File tree

2 files changed

+180
-77
lines changed

2 files changed

+180
-77
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ Copyright (c) 2011-2023 Claudio Satriano <[email protected]>
77
## unreleased
88

99
This release requires at least Python 3.7.
10+
Warning: the SQLite database used by this version is not compatible with
11+
previous versions.
1012

1113
### Input/output
1214

sourcespec/ssp_sqlite_output.py

Lines changed: 178 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -15,62 +15,96 @@
1515
from sourcespec.ssp_setup import ssp_exit
1616
from sourcespec._version import get_versions
1717
logger = logging.getLogger(__name__.split('.')[-1])
18+
# Current supported DB version
19+
DB_VERSION = 2
1820

1921

20-
def _log_db_write_error(db_err, db_file):
21-
logger.error(f'Unable to insert values: {db_err}')
22-
logger.info('Maybe your sqlite database has an old format.')
23-
logger.info('Try to remove or rename your database file.')
24-
logger.info(f'(Current database file: {db_file})')
25-
ssp_exit(1)
22+
def _db_file_exists(db_file):
23+
"""
24+
Check if SQLite database file exists.
2625
26+
:param db_file: SQLite database file
27+
:type db_file: str
28+
:return: True if file exists, False otherwise
29+
:rtype: bool
30+
"""
31+
return os.path.isfile(db_file)
2732

28-
def write_sqlite(config, sspec_output):
29-
try:
30-
database_file = config.database_file
31-
except KeyError:
32-
database_file = None
33-
if not database_file:
34-
return
3533

36-
event = config.event
37-
evid = event.event_id
38-
runid = config.options.run_id
39-
40-
# Current supported DB version
41-
DB_VERSION = 2
34+
def _open_sqlite_db(db_file):
35+
"""
36+
Open SQLite database.
4237
43-
# only check version if database_file exists
44-
check_version = os.path.isfile(database_file)
45-
46-
# Open SQLite database
38+
:param db_file: SQLite database file
39+
:type db_file: str
40+
:return: SQLite connection and cursor
41+
:rtype: tuple
42+
"""
4743
try:
48-
conn = sqlite3.connect(database_file, timeout=60)
44+
conn = sqlite3.connect(db_file, timeout=60)
4945
except Exception as msg:
5046
logger.error(msg)
5147
logger.info(
52-
f'Please check whether "{database_file}" is a valid SQLite file.')
48+
f'Please check whether "{db_file}" is a valid SQLite file.')
5349
ssp_exit(1)
54-
c = conn.cursor()
55-
56-
if check_version:
57-
# Get current DB version
58-
db_version = c.execute('PRAGMA user_version').fetchone()[0]
59-
if db_version < DB_VERSION:
60-
logger.error(
61-
f'"{database_file}" has an old database version: '
62-
f'"{db_version}" Current supported version is "{DB_VERSION}".'
63-
)
64-
logger.info(
65-
'Remove or rename your old database file, '
66-
'so that a new one can be created.'
67-
)
68-
ssp_exit(1)
69-
else:
70-
# Set the DB version
71-
c.execute('PRAGMA user_version = {v:d}'.format(v=DB_VERSION))
50+
return conn, conn.cursor()
51+
52+
53+
def _check_db_version(cursor, db_file):
54+
"""
55+
Check database version.
56+
57+
:param cursor: SQLite cursor
58+
:type cursor: sqlite3.Cursor
59+
:param db_file: SQLite database file
60+
:type db_file: str
61+
"""
62+
db_version = cursor.execute('PRAGMA user_version').fetchone()[0]
63+
if db_version < DB_VERSION:
64+
logger.error(
65+
f'"{db_file}" has an old database version: '
66+
f'"{db_version}" Current supported version is "{DB_VERSION}".'
67+
)
68+
logger.info(
69+
'Remove or rename your old database file, '
70+
'so that a new one can be created.'
71+
)
72+
ssp_exit(1)
73+
74+
75+
def _set_db_version(cursor):
76+
"""
77+
Set database version.
78+
79+
:param cursor: SQLite cursor
80+
:type cursor: sqlite3.Cursor
81+
"""
82+
cursor.execute('PRAGMA user_version = {v:d}'.format(v=DB_VERSION))
83+
84+
85+
def _log_db_write_error(db_err, db_file):
86+
"""
87+
Log database write error.
7288
73-
# Create Station table
89+
:param db_err: database error
90+
:type db_err: Exception
91+
:param db_file: SQLite database file
92+
:type db_file: str
93+
"""
94+
logger.error(f'Unable to insert values: {db_err}')
95+
logger.info('Maybe your sqlite database has an old format.')
96+
logger.info('Try to remove or rename your database file.')
97+
logger.info(f'(Current database file: {db_file})')
98+
ssp_exit(1)
99+
100+
101+
def _create_stations_table(cursor, db_file):
102+
"""
103+
Create Stations table.
104+
105+
:param cursor: SQLite cursor
106+
:type cursor: sqlite3.Cursor
107+
"""
74108
sql_create_stations_table = """CREATE TABLE IF NOT EXISTS Stations (
75109
stid TEXT,
76110
evid TEXT,
@@ -106,24 +140,36 @@ def write_sqlite(config, sspec_output):
106140
Er REAL,
107141
Er_is_outlier INT,
108142
dist REAL,
109-
azimuth REAL
143+
azimuth REAL,
144+
PRIMARY KEY (stid, evid, runid)
110145
);"""
111-
c.execute(sql_create_stations_table)
112-
# Write station source parameters to database
113-
nobs = 0
146+
try:
147+
cursor.execute(sql_create_stations_table)
148+
except Exception as db_err:
149+
_log_db_write_error(db_err, db_file)
150+
151+
152+
def _write_stations_table(cursor, db_file, sspec_output, config):
153+
"""
154+
Write station source parameters to database.
155+
156+
:param cursor: SQLite cursor
157+
:type cursor: sqlite3.Cursor
158+
:param db_file: SQLite database file
159+
:type db_file: str
160+
:param sspec_output: sspec output object
161+
:type sspec_output: ssp_data_types.SourceSpecOutput
162+
:param config: sspec configuration object
163+
:type config: config.Config
164+
"""
165+
event = config.event
166+
evid = event.event_id
167+
runid = config.options.run_id
114168
stationpar = sspec_output.station_parameters
115-
sql_delete_from_stations =\
116-
'DELETE FROM Stations WHERE stid=? AND evid=? AND runid=?;'
169+
nobs = 0
117170
for statId in sorted(stationpar.keys()):
118171
nobs += 1
119172
par = stationpar[statId]
120-
# Remove existing line, if present
121-
t = (statId, evid, runid)
122-
try:
123-
c.execute(sql_delete_from_stations, t)
124-
except Exception as msg:
125-
_log_db_write_error(msg, database_file)
126-
ssp_exit(1)
127173
# Insert new line
128174
t = (
129175
statId, evid, runid,
@@ -148,16 +194,23 @@ def write_sqlite(config, sspec_output):
148194
)
149195
# Create a string like ?,?,?,?
150196
values = ','.join('?' * len(t))
151-
sql_insert_into_stations = f'INSERT INTO Stations VALUES({values});'
197+
sql_insert_into_stations =\
198+
f'INSERT OR REPLACE INTO Stations VALUES({values});'
152199
try:
153-
c.execute(sql_insert_into_stations, t)
200+
cursor.execute(sql_insert_into_stations, t)
154201
except Exception as msg:
155-
_log_db_write_error(msg, database_file)
202+
_log_db_write_error(msg, db_file)
156203
ssp_exit(1)
157-
# Commit changes
158-
conn.commit()
204+
return nobs
205+
159206

160-
# Create Event table
207+
def _create_events_table(cursor, db_file):
208+
"""
209+
Create Events table.
210+
211+
:param cursor: SQLite cursor
212+
:type cursor: sqlite3.Cursor
213+
"""
161214
sql_create_events_table = """CREATE TABLE IF NOT EXISTS Events (
162215
/* Event info */
163216
evid TEXT,
@@ -263,9 +316,33 @@ def write_sqlite(config, sspec_output):
263316
author_email TEXT,
264317
agency_full_name TEXT,
265318
agency_short_name TEXT,
266-
agency_url TEXT
319+
agency_url TEXT,
320+
PRIMARY KEY (evid, runid)
267321
);"""
268-
c.execute(sql_create_events_table)
322+
try:
323+
cursor.execute(sql_create_events_table)
324+
except Exception as db_err:
325+
_log_db_write_error(db_err, db_file)
326+
327+
328+
def _write_events_table(cursor, db_file, sspec_output, config, nobs):
329+
"""
330+
Write Events table.
331+
332+
:param cursor: SQLite cursor
333+
:type cursor: sqlite3.Cursor
334+
:param db_file: SQLite database file
335+
:type db_file: str
336+
:param sspec_output: SSP output object
337+
:type sspec_output: ssp_data_types.SourceSpecOutput
338+
:param config: SSP configuration object
339+
:type config: config.Config
340+
:param nobs: Number of observations
341+
:type nobs: int
342+
"""
343+
event = config.event
344+
evid = event.event_id
345+
runid = config.options.run_id
269346
means = sspec_output.mean_values()
270347
mean_errors = sspec_output.mean_uncertainties()
271348
wmeans = sspec_output.weighted_mean_values()
@@ -274,14 +351,6 @@ def write_sqlite(config, sspec_output):
274351
percentile_errors = sspec_output.percentiles_uncertainties()
275352
run_completed = f'{config.end_of_run} {config.end_of_run_tz}'
276353
ssp_version = get_versions()['version']
277-
# Remove event from Event table, if present
278-
t = (evid, runid)
279-
sql_delete_from_events = 'DELETE FROM Events WHERE evid=? AND runid=?;'
280-
try:
281-
c.execute(sql_delete_from_events, t)
282-
except Exception as msg:
283-
_log_db_write_error(msg, database_file)
284-
ssp_exit(1)
285354
ev_lon = event.hypocenter.longitude.value_in_deg
286355
ev_lat = event.hypocenter.latitude.value_in_deg
287356
ev_depth = event.hypocenter.depth.value_in_km
@@ -370,13 +439,45 @@ def write_sqlite(config, sspec_output):
370439
)
371440
# Create a string like ?,?,?,?
372441
values = ','.join('?' * len(t))
373-
sql_insert_into_events = f'INSERT INTO Events VALUES({values});'
442+
sql_insert_into_events = f'INSERT OR REPLACE INTO Events VALUES({values});'
374443
try:
375-
c.execute(sql_insert_into_events, t)
444+
cursor.execute(sql_insert_into_events, t)
376445
except Exception as msg:
377-
_log_db_write_error(msg, database_file)
446+
_log_db_write_error(msg, db_file)
378447
ssp_exit(1)
448+
449+
450+
def write_sqlite(config, sspec_output):
451+
"""
452+
Write SSP output to SQLite database.
453+
454+
:param config: SSP configuration object
455+
:type config: config.Config
456+
:param sspec_output: SSP output object
457+
:type sspec_output: ssp_data_types.SourceSpecOutput
458+
"""
459+
db_file = config.get('database_file', None)
460+
if not db_file:
461+
return
462+
463+
db_file_exists = _db_file_exists(db_file)
464+
conn, cursor = _open_sqlite_db(db_file)
465+
if db_file_exists:
466+
_check_db_version(cursor, db_file)
467+
else:
468+
_set_db_version(cursor)
469+
470+
# Create Stations table
471+
_create_stations_table(cursor, db_file)
472+
# Write station source parameters to database
473+
nobs = _write_stations_table(cursor, db_file, sspec_output, config)
474+
# Commit changes
475+
conn.commit()
476+
# Create Events table
477+
_create_events_table(cursor, db_file)
478+
# Write event source parameters to database
479+
_write_events_table(cursor, db_file, sspec_output, config, nobs)
379480
# Commit changes and close database
380481
conn.commit()
381482
conn.close()
382-
logger.info(f'Output written to SQLite database: {database_file}')
483+
logger.info(f'Output written to SQLite database: {db_file}')

0 commit comments

Comments
 (0)