Skip to content

Commit 1ced609

Browse files
committed
fix: quote load paths, imports, and transpile inputs
Rebased onto main and squashed to the net change after several rounds of trimming; the sqlfluff and sqlite hunks are in #2148 / #2149. Covers duckdb load-path quoting for mimic-iii, -ed and -note, the postgres datadir/user quoting and concepts build order, transpile UTF-8 handling with its test, and the validate_demo table lists. The mimic-iv duckdb hunk is dropped: main replaced mimic-iv/buildmimic/duckdb/import_duckdb.sh with build_mimic.sh in #1757, so that file no longer exists.
1 parent 8bcbd19 commit 1ced609

12 files changed

Lines changed: 87 additions & 24 deletions

File tree

‎mimic-iii/buildmimic/duckdb/import_duckdb.sh‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,11 @@ make_table_name () {
8585
# load data into database
8686
find "$MIMIC_DIR" -type f -regex '.*\.csv\(.gz\)*' | while IFS= read -r FILE; do
8787
make_table_name "$FILE"
88-
echo "Loading $FILE .. \c"
88+
# Escape single quotes for SQL string literal
89+
FILE_SQL=$(printf '%s' "$FILE" | sed "s/'/''/g")
90+
printf "Loading %s .. " "$FILE"
8991
try duckdb "$OUTFILE" <<-EOSQL
90-
COPY $TABLE_NAME FROM '$FILE' (HEADER, DELIM ',', QUOTE '"', ESCAPE '"');
92+
COPY $TABLE_NAME FROM '$FILE_SQL' (HEADER, DELIM ',', QUOTE '"', ESCAPE '"');
9193
EOSQL
9294
echo "done!"
9395
done && echo "Successfully finished loading data into $OUTFILE."

‎mimic-iii/buildmimic/postgres/Makefile‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ mimic-build-gz:
104104
@echo '------------------'
105105
@echo ''
106106
@sleep 2
107-
psql "$(DBSTRING)" -v ON_ERROR_STOP=1 -f postgres_load_data_gz.sql -v mimic_data_dir=${datadir}
107+
psql "$(DBSTRING)" -v ON_ERROR_STOP=1 -f postgres_load_data_gz.sql -v "mimic_data_dir=$(DATADIR)"
108108
@echo ''
109109
@echo '--------------------'
110110
@echo '-- Adding indexes --'
@@ -151,7 +151,7 @@ mimic-build:
151151
@echo '------------------'
152152
@echo ''
153153
@sleep 2
154-
psql "$(DBSTRING)" -v ON_ERROR_STOP=1 -f postgres_load_data.sql -v mimic_data_dir=${DATADIR}
154+
psql "$(DBSTRING)" -v ON_ERROR_STOP=1 -f postgres_load_data.sql -v "mimic_data_dir=$(DATADIR)"
155155
@echo ''
156156
@echo '--------------------'
157157
@echo '-- Adding indexes --'
@@ -184,15 +184,15 @@ ifeq ("$(physionetuser)","")
184184
@echo 'Call the makefile again with physionetuser=<USERNAME>'
185185
@echo ' e.g. make eicu-download datadir=/path/to/data physionetuser=hello@physionet.org'
186186
else
187-
wget --user $(physionetuser) --ask-password -P $(DATADIR) -A csv.gz -m -p -E -k -K -np -nd "$(PHYSIONETURL)"
187+
wget --user $(physionetuser) --ask-password -P "$(DATADIR)" -A csv.gz -m -p -E -k -K -np -nd "$(PHYSIONETURL)"
188188
endif
189189

190190
mimic-demo-download:
191191
@echo '------------------------------------------'
192192
@echo '-- Downloading MIMIC-III from PhysioNet --'
193193
@echo '------------------------------------------'
194194
@echo ''
195-
wget --user $(physionetuser) --ask-password -P $(DATADIR) -A csv.gz -m -p -E -k -K -np -nd "$(PHYSIONETDEMOURL)"
195+
wget --user $(physionetuser) --ask-password -P "$(DATADIR)" -A csv.gz -m -p -E -k -K -np -nd "$(PHYSIONETDEMOURL)"
196196

197197
#This is fairly inelegant and could be tidied with a for loop and an if to check for gzip,
198198
#but need to maintain compatibility with Windows, which baffling lacks these things

‎mimic-iii/buildmimic/postgres/create_mimic_user.sh‎

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ else
2020
echo "User is set to '$MIMIC_USER'";
2121
fi
2222

23+
# escape ' in password for SQL string literal
24+
MIMIC_PASSWORD_SQL=$(printf '%s' "$MIMIC_PASSWORD" | sed "s/'/''/g")
25+
# quote SQL identifiers (double any embedded ")
26+
sql_ident () { printf '%s' "$1" | sed 's/"/""/g; s/^/"/; s/$/"/'; }
27+
MIMIC_USER_SQL=$(sql_ident "$MIMIC_USER")
28+
MIMIC_DB_SQL=$(sql_ident "$MIMIC_DB")
29+
2330
PSQL='psql'
2431

2532
# add in the host/port, if they were specified (not null, -n)
@@ -58,11 +65,11 @@ fi
5865
if [ "$MIMIC_USER" != "postgres" ]; then
5966
# we need to create this user via postgres
6067
# use SUDO to login as postgres
61-
$PSQL -U postgres -d postgres -c "DROP USER IF EXISTS $MIMIC_USER; CREATE USER $MIMIC_USER WITH PASSWORD '$MIMIC_PASSWORD';"
68+
$PSQL -U postgres -d postgres -c "DROP USER IF EXISTS $MIMIC_USER_SQL; CREATE USER $MIMIC_USER_SQL WITH PASSWORD '$MIMIC_PASSWORD_SQL';"
6269
fi
6370

6471
if [ "$MIMIC_DB" != "postgres" ]; then
6572
# drop and recreate the database
66-
$PSQL -U postgres -d postgres -c "DROP DATABASE IF EXISTS $MIMIC_DB;"
67-
$PSQL -U postgres -d postgres -c "CREATE DATABASE $MIMIC_DB OWNER $MIMIC_USER;"
68-
fi
73+
$PSQL -U postgres -d postgres -c "DROP DATABASE IF EXISTS $MIMIC_DB_SQL;"
74+
$PSQL -U postgres -d postgres -c "CREATE DATABASE $MIMIC_DB_SQL OWNER $MIMIC_USER_SQL;"
75+
fi

‎mimic-iii/concepts_duckdb/duckdb.sql‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,10 @@
9393
.read pivot/pivoted_vital.sql
9494
.print 'pivot/pivoted_bg_art.sql'
9595
.read pivot/pivoted_bg_art.sql
96-
.print 'pivot/pivoted_sofa.sql'
97-
.read pivot/pivoted_sofa.sql
9896
.print 'pivot/pivoted_oasis.sql'
9997
.read pivot/pivoted_oasis.sql
98+
.print 'pivot/pivoted_sofa.sql'
99+
.read pivot/pivoted_sofa.sql
100100

101101
-- comorbidity
102102
.print 'comorbidity/elixhauser_ahrq_v37.sql'

‎mimic-iii/concepts_postgres/postgres-make-concepts.sql‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ SET search_path TO mimiciii_derived, mimiciii;
6161
\i pivot/pivoted_uo.sql
6262
\i pivot/pivoted_vital.sql
6363
\i pivot/pivoted_bg_art.sql
64-
\i pivot/pivoted_sofa.sql
6564
\i pivot/pivoted_oasis.sql
65+
\i pivot/pivoted_sofa.sql
6666

6767
-- comorbidity
6868
\i comorbidity/elixhauser_ahrq_v37.sql

‎mimic-iv-ed/buildmimic/duckdb/import_duckdb.sh‎

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ usage () {
2929
die "
3030
USAGE: ./import_duckdb.sh mimic_data_dir [output_db]
3131
WHERE:
32-
mimic_data_dir directory that contains csv.gz or csv files
32+
mimic_data_dir directory that contains csv.tar.gz or csv files
3333
output_db: optional filename for duckdb file (default: mimic4_ed.db)\
3434
"
3535
}
@@ -94,18 +94,29 @@ make_table_name () {
9494

9595

9696
# load data into database
97-
find "$MIMIC_DIR" -type f -name '*.csv???' | sort | while IFS= read -r FILE; do
97+
# Match both .csv and .csv.gz ( '*.csv???' only matched .csv.gz ).
98+
LOAD_COUNT_FILE=$(mktemp) || die "mktemp failed"
99+
trap 'rm -f "$LOAD_COUNT_FILE"' EXIT
100+
: > "$LOAD_COUNT_FILE"
101+
find "$MIMIC_DIR" -type f \( -name '*.csv' -o -name '*.csv.gz' \) | sort | while IFS= read -r FILE; do
98102
make_table_name "$FILE"
99103

100104
# skip directories which we do not expect in mimic-iv-ed
101105
# avoids syntax errors if mimic-iv in the same dir
102-
case $DIRNAME in
106+
case "$DIRNAME" in
103107
(ed) ;; # OK
104108
(*) continue;
105109
esac
110+
FILE_SQL=$(printf '%s' "$FILE" | sed "s/'/''/g")
106111
echo "Loading $FILE .. "
107112
try duckdb "$OUTFILE" <<-EOSQL
108-
COPY $TABLE_NAME FROM '$FILE' (HEADER, DELIM ',', QUOTE '"', ESCAPE '"');
113+
COPY $TABLE_NAME FROM '$FILE_SQL' (HEADER, DELIM ',', QUOTE '"', ESCAPE '"');
109114
EOSQL
115+
echo x >> "$LOAD_COUNT_FILE"
110116
echo "done!"
111-
done && echo "Successfully finished loading data into $OUTFILE."
117+
done || exit $?
118+
119+
if [ ! -s "$LOAD_COUNT_FILE" ]; then
120+
die "No .csv / .csv.gz files loaded from $MIMIC_DIR (expected ed/)."
121+
fi
122+
echo "Successfully finished loading data into $OUTFILE."

‎mimic-iv-note/buildmimic/duckdb/import_duckdb.sh‎

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ usage () {
2929
die "
3030
USAGE: ./import_duckdb.sh mimic_data_dir [output_db]
3131
WHERE:
32-
mimic_data_dir directory that contains csv.gz or csv files
32+
mimic_data_dir directory that contains csv.tar.gz or csv files
3333
output_db: optional filename for duckdb file (default: mimic4_note.db)\
3434
"
3535
}
@@ -97,18 +97,23 @@ make_table_name () {
9797

9898

9999
# load data into database
100-
find "$MIMIC_DIR" -type f -name '*.csv???' | sort | while IFS= read -r FILE; do
100+
# Match both .csv and .csv.gz ( '*.csv???' only matched .csv.gz ).
101+
LOAD_COUNT_FILE=$(mktemp) || die "mktemp failed"
102+
trap 'rm -f "$LOAD_COUNT_FILE"' EXIT
103+
: > "$LOAD_COUNT_FILE"
104+
find "$MIMIC_DIR" -type f \( -name '*.csv' -o -name '*.csv.gz' \) | sort | while IFS= read -r FILE; do
101105
make_table_name "$FILE"
102106

103107
# skip directories which we do not expect in mimic-iv-note
104108
# avoids syntax errors if mimic-iv in the same dir
105-
case $DIRNAME in
109+
case "$DIRNAME" in
106110
(note) ;; # OK
107111
(*) continue;
108112
esac
113+
FILE_SQL=$(printf '%s' "$FILE" | sed "s/'/''/g")
109114
echo "Loading $FILE .."
110115
OUTPUT=$(duckdb "$OUTFILE" 2>&1 <<-EOSQL
111-
COPY $TABLE_NAME FROM '$FILE' (HEADER, DELIM ',', QUOTE '"', ESCAPE '"');
116+
COPY $TABLE_NAME FROM '$FILE_SQL' (HEADER, DELIM ',', QUOTE '"', ESCAPE '"');
112117
EOSQL
113118
)
114119
# If the table is missing in the DB, we emit a warning and continue.
@@ -123,5 +128,11 @@ EOSQL
123128
yell "$OUTPUT"
124129
die "Exiting due to load error."
125130
fi
131+
echo x >> "$LOAD_COUNT_FILE"
126132
echo "done!"
127-
done && echo "Successfully finished loading data into $OUTFILE."
133+
done || exit $?
134+
135+
if [ ! -s "$LOAD_COUNT_FILE" ]; then
136+
die "No .csv / .csv.gz files loaded from $MIMIC_DIR (expected note/)."
137+
fi
138+
echo "Successfully finished loading data into $OUTFILE."

‎mimic-iv/buildmimic/mysql/validate_demo.sql‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,17 @@ FROM (
3131
SELECT 'poe_detail' AS tbl, 3795 AS row_count UNION ALL
3232
SELECT 'prescriptions' AS tbl, 18087 AS row_count UNION ALL
3333
SELECT 'procedures_icd' AS tbl, 722 AS row_count UNION ALL
34+
SELECT 'provider' AS tbl, 40508 AS row_count UNION ALL
3435
SELECT 'services' AS tbl, 319 AS row_count UNION ALL
3536
SELECT 'transfers' AS tbl, 1190 AS row_count UNION ALL
3637
-- icu data
3738
SELECT 'icustays' AS tbl, 140 AS row_count UNION ALL
39+
SELECT 'caregiver' AS tbl, 15468 AS row_count UNION ALL
3840
SELECT 'd_items' AS tbl, 4014 AS row_count UNION ALL
3941
SELECT 'chartevents' AS tbl, 668862 AS row_count UNION ALL
4042
SELECT 'datetimeevents' AS tbl, 15280 AS row_count UNION ALL
4143
SELECT 'inputevents' AS tbl, 20404 AS row_count UNION ALL
44+
SELECT 'ingredientevents' AS tbl, 25728 AS row_count UNION ALL
4245
SELECT 'outputevents' AS tbl, 9362 AS row_count UNION ALL
4346
SELECT 'procedureevents' AS tbl, 1468 AS row_count
4447
) exp
@@ -64,14 +67,17 @@ INNER JOIN
6467
SELECT 'poe_detail' AS tbl, count(*) AS row_count FROM poe_detail UNION ALL
6568
SELECT 'prescriptions' AS tbl, count(*) AS row_count FROM prescriptions UNION ALL
6669
SELECT 'procedures_icd' AS tbl, count(*) AS row_count FROM procedures_icd UNION ALL
70+
SELECT 'provider' AS tbl, count(*) AS row_count FROM provider UNION ALL
6771
SELECT 'services' AS tbl, count(*) AS row_count FROM services UNION ALL
6872
SELECT 'transfers' AS tbl, count(*) AS row_count FROM transfers UNION ALL
6973
-- icu data
7074
SELECT 'icustays' AS tbl, count(*) AS row_count FROM icustays UNION ALL
75+
SELECT 'caregiver' AS tbl, count(*) AS row_count FROM caregiver UNION ALL
7176
SELECT 'chartevents' AS tbl, count(*) AS row_count FROM chartevents UNION ALL
7277
SELECT 'd_items' AS tbl, count(*) AS row_count FROM d_items UNION ALL
7378
SELECT 'datetimeevents' AS tbl, count(*) AS row_count FROM datetimeevents UNION ALL
7479
SELECT 'inputevents' AS tbl, count(*) AS row_count FROM inputevents UNION ALL
80+
SELECT 'ingredientevents' AS tbl, count(*) AS row_count FROM ingredientevents UNION ALL
7581
SELECT 'outputevents' AS tbl, count(*) AS row_count FROM outputevents UNION ALL
7682
SELECT 'procedureevents' AS tbl, count(*) AS row_count FROM procedureevents
7783
) obs

‎mimic-iv/buildmimic/postgres/validate_demo.sql‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,17 @@ WITH expected AS
2222
SELECT 'poe_detail' AS tbl, 3795 AS row_count UNION ALL
2323
SELECT 'prescriptions' AS tbl, 18087 AS row_count UNION ALL
2424
SELECT 'procedures_icd' AS tbl, 722 AS row_count UNION ALL
25+
SELECT 'provider' AS tbl, 40508 AS row_count UNION ALL
2526
SELECT 'services' AS tbl, 319 AS row_count UNION ALL
2627
SELECT 'transfers' AS tbl, 1190 AS row_count UNION ALL
2728
-- icu data
2829
SELECT 'icustays' AS tbl, 140 AS row_count UNION ALL
30+
SELECT 'caregiver' AS tbl, 15468 AS row_count UNION ALL
2931
SELECT 'd_items' AS tbl, 4014 AS row_count UNION ALL
3032
SELECT 'chartevents' AS tbl, 668862 AS row_count UNION ALL
3133
SELECT 'datetimeevents' AS tbl, 15280 AS row_count UNION ALL
3234
SELECT 'inputevents' AS tbl, 20404 AS row_count UNION ALL
35+
SELECT 'ingredientevents' AS tbl, 25728 AS row_count UNION ALL
3336
SELECT 'outputevents' AS tbl, 9362 AS row_count UNION ALL
3437
SELECT 'procedureevents' AS tbl, 1468 AS row_count
3538
)
@@ -54,14 +57,17 @@ WITH expected AS
5457
SELECT 'poe_detail' AS tbl, count(*) AS row_count FROM mimiciv_hosp.poe_detail UNION ALL
5558
SELECT 'prescriptions' AS tbl, count(*) AS row_count FROM mimiciv_hosp.prescriptions UNION ALL
5659
SELECT 'procedures_icd' AS tbl, count(*) AS row_count FROM mimiciv_hosp.procedures_icd UNION ALL
60+
SELECT 'provider' AS tbl, count(*) AS row_count FROM mimiciv_hosp.provider UNION ALL
5761
SELECT 'services' AS tbl, count(*) AS row_count FROM mimiciv_hosp.services UNION ALL
5862
SELECT 'transfers' AS tbl, count(*) AS row_count FROM mimiciv_hosp.transfers UNION ALL
5963
-- icu data
6064
SELECT 'icustays' AS tbl, count(*) AS row_count FROM mimiciv_icu.icustays UNION ALL
65+
SELECT 'caregiver' AS tbl, count(*) AS row_count FROM mimiciv_icu.caregiver UNION ALL
6166
SELECT 'chartevents' AS tbl, count(*) AS row_count FROM mimiciv_icu.chartevents UNION ALL
6267
SELECT 'd_items' AS tbl, count(*) AS row_count FROM mimiciv_icu.d_items UNION ALL
6368
SELECT 'datetimeevents' AS tbl, count(*) AS row_count FROM mimiciv_icu.datetimeevents UNION ALL
6469
SELECT 'inputevents' AS tbl, count(*) AS row_count FROM mimiciv_icu.inputevents UNION ALL
70+
SELECT 'ingredientevents' AS tbl, count(*) AS row_count FROM mimiciv_icu.ingredientevents UNION ALL
6571
SELECT 'outputevents' AS tbl, count(*) AS row_count FROM mimiciv_icu.outputevents UNION ALL
6672
SELECT 'procedureevents' AS tbl, count(*) AS row_count FROM mimiciv_icu.procedureevents
6773
)

‎src/mimic_utils/sqlglot_dialects/postgres.py‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,11 @@ def _unit(expression: exp.Expression, default: str = "DAY") -> str:
2323
# The logic is as follows:
2424
# * DAY -> difference of the two calendar dates (date subtraction = whole days)
2525
# * YEAR -> difference of the two calendar years
26+
# * MONTH -> year*12 + month difference (calendar month boundaries)
2627
# * sub-day units -> truncate both operands to the unit (which makes the
2728
# elapsed seconds an exact multiple of the unit) then divide.
29+
# WEEK is intentionally unsupported: BigQuery week starts (SUNDAY/ISO) do not
30+
# match PostgreSQL DATE_TRUNC('week') Monday semantics.
2831
# https://cloud.google.com/bigquery/docs/reference/standard-sql/datetime_functions#datetime_diff
2932
_SECONDS_PER_UNIT = {"SECOND": 1, "MINUTE": 60, "HOUR": 3600}
3033

@@ -39,6 +42,19 @@ def _datetime_diff_sql(self: Postgres.Generator, expression: exp.Expression) ->
3942
return f"(CAST({end} AS DATE) - CAST({start} AS DATE))"
4043
if unit == "YEAR":
4144
return f"CAST(EXTRACT(YEAR FROM {end}) - EXTRACT(YEAR FROM {start}) AS BIGINT)"
45+
if unit == "MONTH":
46+
# Calendar month boundaries (matches BigQuery DATETIME_DIFF MONTH).
47+
return (
48+
"CAST((EXTRACT(YEAR FROM {end}) - EXTRACT(YEAR FROM {start})) * 12 "
49+
"+ (EXTRACT(MONTH FROM {end}) - EXTRACT(MONTH FROM {start})) AS BIGINT)"
50+
).format(end=end, start=start)
51+
if unit not in _SECONDS_PER_UNIT:
52+
# WEEK (and WEEK(SUNDAY)/ISO) need BigQuery's week-start semantics;
53+
# refuse rather than emit a silently wrong days/7 division.
54+
raise ValueError(
55+
f"Unsupported DATETIME_DIFF unit {unit!r}; "
56+
"expected SECOND, MINUTE, HOUR, DAY, MONTH, or YEAR"
57+
)
4258

4359
lo = unit.lower()
4460
factor = _SECONDS_PER_UNIT[unit]

0 commit comments

Comments
 (0)