You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

93 lines
3.0 KiB
JavaScript

"use strict";
// TODO: Make this its own independent package once the last bits have been polished (eg. consistent type names and onUpdate/onDelete values)
const assert = require("assert");
module.exports = async function knexIntrospect(knex, tableName) {
let clientType = knex.client.config.client;
assert(clientType === "sqlite3" || clientType === "pg");
assert(/^[a-zA_Z_]+$/.test(tableName));
let safeTableName = tableName; // FIXME: Sanitize instead of assert?
let tableLayout = {};
if (clientType === "sqlite3") {
let [ columns, foreignKeys ] = await Promise.all([
knex.raw(`PRAGMA table_info('${safeTableName}')`),
knex.raw(`PRAGMA foreign_key_list('${safeTableName}')`)
]);
for (let column of columns) {
tableLayout[column.name] = {
name: column.name,
type: column.type,
notNull: (column.notnull === 1),
isPrimaryKey: (column.pk === 1),
foreignKey: null
};
}
for (let fk of foreignKeys) {
tableLayout[fk.from].foreignKey = {
table: fk.table,
column: fk.to,
onUpdate: fk.on_update,
onDelete: fk.on_delete
};
}
} else if (clientType === "pg") {
// Yes, this is a lot of queries. Yes, I "should" have used a single query with JOINs. If I did that, I would now have 1) a headache and 2) no working code. If this is something you care about, feel free to submit a PR.
let tables = await knex("pg_class").limit(1).where({ relname: tableName });
assert(tables.length === 1);
let table = tables[0];
let columns = await knex("pg_attribute")
.where({ attrelid: table.oid })
.where("attnum", ">", 0);
for (let column of columns) {
let constraints = await knex("pg_constraint")
.where({ conrelid: table.oid })
.whereRaw(`conkey = ARRAY[?::SMALLINT]`, [ column.attnum ]);
let primaryKeyConstraint, foreignKey;
for (let constraint of constraints) {
if (constraint.contype === "p") {
primaryKeyConstraint = constraint;
} else if (constraint.contype === "f") {
// TODO: Can there be multiple foreign key constraints?
let foreignTable = await knex("pg_class").limit(1).where({ oid: constraint.confrelid });
// TODO: Support composite keys?
assert(constraint.confkey.length === 1);
let foreignColumn = await knex("pg_attribute")
.limit(1)
.where({ attrelid: constraint.confrelid })
.whereRaw(`attnum = ?::SMALLINT`, [ constraint.confkey[0] ]);
foreignKey = {
table: foreignTable[0].relname,
column: foreignColumn[0].attname,
// TODO: Convert possible values for onUpdate/onDelete into something consistent with SQLite, using match-value
onUpdate: (constraint.confupdtype === "a") ? null : constraint.confupdtype,
onDelete: (constraint.confdeltype === "a") ? null : constraint.confdeltype
};
}
}
tableLayout[column.attname] = {
name: column.attname,
type: (await knex("pg_type").limit(1).where({ oid: column.atttypid }))[0].typname,
notNull: column.attnotnull,
isPrimaryKey: (primaryKeyConstraint != null),
foreignKey: foreignKey
};
}
}
return tableLayout;
};