comment = async (knex, action, rawQuotedComment, target) => {
  const { schemaName, tableName } = target;
  // NOTE: This does not escape any embedded single quotes!
  // (Since we control all the inputs here, we know we don't need to.)
  const lines = [
    `EXEC sp_${action}extendedproperty`,
    " @name = N'MS_Description'",
    action !== 'drop' ? `, @value = ${rawQuotedComment}` : '',
    `, @level0type = N'Schema', @level0name = N'${schemaName}'`,
    `, @level1type = N'Table', @level1name = N'${tableName}'`,
    target.columnName !== undefined
      ? `, @level2type = N'Column', @level2name = N'${target.columnName}'`
      : '',
  ];
  return await knex.schema.raw(lines.join(''));
}
@param {Knex} knex
@param {'add' | 'update' | 'drop'} action
@param {string} rawQuotedComment No escaping is done. This should include any needed quote marks.
@param {{schemaName: string, tableName: string, columnName?: string}} target