information_schema.columns omits materialized views -- they are not in the
SQL standard -- and gs.osm_skinny is one. So the source this app runs on
looked like it had no columns at all: creating a version failed with "No
usable columns in col_meta" while col_meta plainly held thirty-six, and
registering such a source would have seeded nothing.
RELATION_COLUMNS_SQL returns the same shape information_schema did, so
mapType and every caller are unchanged: data_type is format_type with the
modifier stripped, which spells things the same way ('character varying',
'numeric'), and precision and scale are unpacked from atttypmod as
information_schema does internally. Verified against the live matview -- 36
usable columns, and the types map to exactly what fc_osm_skinny_29 already
has.
The table browser had the same blind spot from information_schema.tables and
now lists from pg_class by relkind, so a materialized view can be registered
rather than merely used by a source registered when it was still a table.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
54 lines
2.0 KiB
JavaScript
54 lines
2.0 KiB
JavaScript
const express = require('express');
|
|
const { RELATION_COLUMNS_SQL } = require('../lib/utils');
|
|
|
|
module.exports = function(pool) {
|
|
const router = express.Router();
|
|
|
|
// list all non-system tables with row estimates
|
|
router.get('/tables', async (req, res) => {
|
|
try {
|
|
const result = await pool.query(`
|
|
-- pg_class, not information_schema.tables, which omits
|
|
-- materialized views: gs.osm_skinny is one, and the browser
|
|
-- could not offer what the app is already built on.
|
|
SELECT
|
|
n.nspname AS schema,
|
|
c.relname AS tname,
|
|
c.reltuples::bigint AS row_estimate
|
|
FROM pg_class c
|
|
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
WHERE c.relkind IN ('r', 'v', 'm', 'f', 'p')
|
|
AND n.nspname NOT IN ('pg_catalog', 'information_schema', 'pf')
|
|
ORDER BY n.nspname, c.relname
|
|
`);
|
|
res.json(result.rows);
|
|
} catch (err) {
|
|
console.error(err);
|
|
res.status(500).json({ error: err.message });
|
|
}
|
|
});
|
|
|
|
// preview a table: column list + 5 sample rows
|
|
router.get('/tables/:schema/:tname/preview', async (req, res) => {
|
|
const { schema, tname } = req.params;
|
|
if (!/^\w+$/.test(schema) || !/^\w+$/.test(tname)) {
|
|
return res.status(400).json({ error: 'Invalid schema or table name' });
|
|
}
|
|
try {
|
|
const cols = await pool.query(
|
|
`${RELATION_COLUMNS_SQL} ORDER BY ordinal_position`, [schema, tname]);
|
|
|
|
const rows = await pool.query(
|
|
`SELECT * FROM ${schema}.${tname} LIMIT 5`
|
|
);
|
|
|
|
res.json({ columns: cols.rows, rows: rows.rows });
|
|
} catch (err) {
|
|
console.error(err);
|
|
res.status(500).json({ error: err.message });
|
|
}
|
|
});
|
|
|
|
return router;
|
|
};
|