|
2 | 2 |
|
3 | 3 | const debug = require('debug')('gtfs-via-postgres')
|
4 | 4 | const sequencify = require('sequencify')
|
5 |
| -const {inspect} = require('util') |
| 5 | +const {Database} = require('duckdb') |
| 6 | +const {promisify} = require('util') |
6 | 7 | const readCsv = require('gtfs-utils/read-csv')
|
7 |
| -const {Stringifier} = require('csv-stringify') |
8 | 8 | const formatters = require('./lib')
|
9 | 9 | const getDependencies = require('./lib/deps')
|
10 | 10 | const pkg = require('./package.json')
|
11 | 11 |
|
12 |
| -const convertGtfsToSql = async function* (files, opt = {}) { |
| 12 | +const convertGtfsToSql = async (pathToDb, files, opt = {}) => { |
| 13 | + debug('pathToDb', pathToDb) |
| 14 | + |
13 | 15 | opt = {
|
14 | 16 | silent: false,
|
15 | 17 | requireDependencies: false,
|
@@ -41,38 +43,6 @@ const convertGtfsToSql = async function* (files, opt = {}) {
|
41 | 43 | debug('deps', deps)
|
42 | 44 |
|
43 | 45 | const tasks = { // file name -> [dep name]
|
44 |
| - 'is_bcp_47_code': { |
45 |
| - dep: [], |
46 |
| - }, |
47 |
| - 'is_timezone': { |
48 |
| - dep: [], |
49 |
| - }, |
50 |
| - ...(tripsWithoutShapeId ? {} : { |
51 |
| - 'shape_exists': { |
52 |
| - dep: [...deps.shape_exists], |
53 |
| - }, |
54 |
| - }), |
55 |
| - |
56 |
| - // special handling of calendar/calendar_dates: |
57 |
| - // service_days relies on *both* calendar's & calendar_dates' tables to |
58 |
| - // be present, so we add mock tasks here. Each of these mock tasks get |
59 |
| - // replaced by a file-based one below if the file has been passed. |
60 |
| - 'calendar': { |
61 |
| - dep: [], |
62 |
| - }, |
63 |
| - 'calendar_dates': { |
64 |
| - dep: [], |
65 |
| - }, |
66 |
| - 'service_days': { |
67 |
| - dep: ['calendar', 'calendar_dates'], |
68 |
| - }, |
69 |
| - |
70 |
| - // The arrivals_departures & connections views rely on frequencies' table |
71 |
| - // to be present, so we add a mock task here. It gets replaced by a |
72 |
| - // file-based one below if the file has been passed. |
73 |
| - 'frequencies': { |
74 |
| - dep: [...deps.frequencies], |
75 |
| - }, |
76 | 46 | }
|
77 | 47 |
|
78 | 48 | for (const file of files) {
|
@@ -100,96 +70,26 @@ const convertGtfsToSql = async function* (files, opt = {}) {
|
100 | 70 | sequencify(tasks, Object.keys(tasks), order)
|
101 | 71 | debug('order', order)
|
102 | 72 |
|
103 |
| - yield `\ |
104 |
| --- GTFS SQL dump generated by ${pkg.name} v${pkg.version} |
105 |
| --- ${pkg.homepage} |
106 |
| --- options: |
107 |
| -${inspect(opt, {compact: false}).split('\n').map(line => '-- ' + line).join('\n')} |
108 |
| -
|
109 |
| -\\set ON_ERROR_STOP True |
110 |
| -CREATE EXTENSION IF NOT EXISTS postgis; |
111 |
| -${opt.schema !== 'public' ? `CREATE SCHEMA IF NOT EXISTS "${opt.schema}";` : ''} |
112 |
| -BEGIN; |
113 |
| -
|
114 |
| -\n` |
| 73 | + const db = new Database(pathToDb) |
| 74 | + const dbRun = promisify(db.run) |
115 | 75 |
|
116 |
| - const csv = new Stringifier({quoted: true}) |
| 76 | + await dbRun('BEGIN TRANSACTION') |
117 | 77 |
|
118 | 78 | for (const name of order) {
|
119 | 79 | if (!silent) console.error(name)
|
120 | 80 | const task = tasks[name]
|
121 |
| - yield `-- ${name}\n-----------------\n\n` |
122 | 81 |
|
123 |
| - const { |
124 |
| - beforeAll, |
125 |
| - afterAll, |
126 |
| - } = formatters[name] |
127 |
| - |
128 |
| - if ('string' === typeof beforeAll && beforeAll) { |
129 |
| - yield beforeAll |
130 |
| - } else if ('function' === typeof beforeAll) { |
131 |
| - yield beforeAll(opt) |
132 |
| - } |
| 82 | + const importData = formatters[name] |
133 | 83 |
|
134 | 84 | if (task.file) {
|
135 |
| - const {formatRow} = formatters[name] |
136 |
| - let nrOfRows = 0 |
137 |
| - for await (const rawRow of await readCsv(task.file)) { |
138 |
| - const row = formatRow(rawRow, opt) |
139 |
| - let formattedRow = null |
140 |
| - csv.api.__transform(row, (_formattedRow) => { |
141 |
| - formattedRow = _formattedRow |
142 |
| - }) |
143 |
| - yield formattedRow |
144 |
| - nrOfRows++ |
145 |
| - } |
146 |
| - |
147 |
| - if (!silent) console.error(` processed ${nrOfRows} rows`) |
148 |
| - } |
149 |
| - |
150 |
| - if ('string' === typeof afterAll && afterAll) { |
151 |
| - yield afterAll + ';\n' |
152 |
| - } else if ('function' === typeof afterAll) { |
153 |
| - yield afterAll(opt) + ';\n' |
| 85 | + const input = await readCsv(task.file) |
| 86 | + await importData(db, input, opt) |
| 87 | + } else { |
| 88 | + await importData(db, opt) |
154 | 89 | }
|
155 | 90 | }
|
156 | 91 |
|
157 |
| - yield `\ |
158 |
| -
|
159 |
| -${opt.postgraphile ? `\ |
160 |
| --- seal imported data |
161 |
| --- todo: |
162 |
| --- > Be careful with public schema.It already has a lot of default privileges that you maybe don't want... See documentation[1]. |
163 |
| --- > [1]: postgresql.org/docs/11/ddl-schemas.html#DDL-SCHEMAS-PRIV |
164 |
| -DO $$ |
165 |
| -BEGIN |
166 |
| - -- https://stackoverflow.com/questions/8092086/create-postgresql-role-user-if-it-doesnt-exist#8099557 |
167 |
| - IF EXISTS ( |
168 |
| - SELECT FROM pg_catalog.pg_roles |
169 |
| - WHERE rolname = 'postgraphile' |
170 |
| - ) THEN |
171 |
| - RAISE NOTICE 'Role "postgraphile" already exists, skipping creation.'; |
172 |
| - ELSE |
173 |
| - CREATE ROLE postgraphile LOGIN PASSWORD 'todo'; -- todo: postgraphile password? |
174 |
| - END IF; |
175 |
| -END |
176 |
| -$$; |
177 |
| -DO $$ |
178 |
| - DECLARE |
179 |
| - db TEXT := current_database(); |
180 |
| - BEGIN |
181 |
| - EXECUTE format('GRANT ALL PRIVILEGES ON DATABASE %I TO %I', db, 'postgraphile'); |
182 |
| - END |
183 |
| -$$; |
184 |
| -GRANT USAGE ON SCHEMA "${opt.schema}" TO postgraphile; |
185 |
| --- https://stackoverflow.com/questions/760210/how-do-you-create-a-read-only-user-in-postgresql#comment50679407_762649 |
186 |
| -REVOKE CREATE ON SCHEMA "${opt.schema}" FROM PUBLIC; |
187 |
| -GRANT SELECT ON ALL TABLES IN SCHEMA "${opt.schema}" TO postgraphile; |
188 |
| --- ALTER DEFAULT PRIVILEGES IN SCHEMA "${opt.schema}" GRANT SELECT ON TABLES TO postgraphile; |
189 |
| --- todo: set search_path? https://stackoverflow.com/questions/760210/how-do-you-create-a-read-only-user-in-postgresql#comment33535263_762649 |
190 |
| -` : ''} |
191 |
| -
|
192 |
| -COMMIT;` |
| 92 | + await dbRun('COMMIT') |
193 | 93 | }
|
194 | 94 |
|
195 | 95 | module.exports = convertGtfsToSql
|
0 commit comments