// derive forecast table name from source tname + version id function fcTable(tname, versionId) { return `pf.fc_${tname}_${versionId}`; } // The columns of a relation, from pg_catalog rather than information_schema. // // information_schema.columns omits materialized views -- they are not in the // SQL standard -- so a source built on one looked like it had no columns at // all: registering it seeded nothing, and creating a version failed with "No // usable columns in col_meta" while col_meta plainly had thirty-six. // // The shape matches what information_schema returned, so mapType and the // callers did not have to change. data_type is format_type with the modifier // stripped, which gives the same spelling information_schema uses ('character // varying', 'numeric'), and the numeric precision and scale are unpacked from // atttypmod the way information_schema does internally. // // Takes $1 = schema, $2 = relation name. const RELATION_COLUMNS_SQL = ` SELECT a.attname AS column_name ,regexp_replace(format_type(a.atttypid, a.atttypmod), '\\(.*\\)$', '') AS data_type ,a.attnum AS ordinal_position ,CASE WHEN a.attnotnull THEN 'NO' ELSE 'YES' END AS is_nullable ,CASE WHEN a.atttypid = 'numeric'::regtype AND a.atttypmod > 4 THEN ((a.atttypmod - 4) >> 16) & 65535 END AS numeric_precision ,CASE WHEN a.atttypid = 'numeric'::regtype AND a.atttypmod > 4 THEN (a.atttypmod - 4) & 65535 END AS numeric_scale FROM pg_attribute a JOIN pg_class c ON c.oid = a.attrelid JOIN pg_namespace n ON n.oid = c.relnamespace WHERE TRUE AND n.nspname = $1 AND c.relname = $2 AND c.relkind IN ('r', 'v', 'm', 'f', 'p') AND a.attnum > 0 AND NOT a.attisdropped `; // map a data_type name to a clean postgres column type function mapType(dataType, numericPrecision, numericScale) { switch (dataType) { case 'character varying': case 'character': case 'text': return 'text'; case 'smallint': case 'integer': return 'integer'; case 'bigint': return 'bigint'; case 'numeric': case 'decimal': return (numericPrecision) ? `numeric(${numericPrecision}, ${numericScale || 0})` : 'numeric'; case 'real': case 'double precision': return 'numeric'; case 'date': return 'date'; case 'timestamp without time zone': return 'timestamp'; case 'timestamp with time zone': return 'timestamptz'; case 'boolean': return 'boolean'; default: return 'text'; } } module.exports = { fcTable, mapType, RELATION_COLUMNS_SQL };