diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2da31c0f21..cbd6e095bf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,6 +17,17 @@ npm i -g ts-node
 npm run migrate
 ```
 
+11.22.0 (2019/06/18)
+--------------------
+### ✨Improvements
+* 管理画面でデータベースの各テーブルのレコード数やサイズを確認できるように
+* サーバー情報にPostgreSQLのバージョンを追加
+
+### 🐛Fixes
+* リモートファイルのダウンロードに失敗することがある問題を修正
+* アンケートの期間を日時指定で選択すると日時がUTCになってしまう問題を修正
+* MFMのパースを修正
+
 11.21.0 (2019/06/16)
 --------------------
 ### ✨Improvements
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 7053360a11..d9223b1c4b 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1226,8 +1226,12 @@ admin/views/index.vue:
   abuse: "スパム報告"
   queue: "ジョブキュー"
   logs: "ログ"
+  db: "データベース"
   back-to-misskey: "Misskeyに戻る"
 
+admin/views/db.vue:
+  tables: "テーブル"
+
 admin/views/dashboard.vue:
   dashboard: "ダッシュボード"
   accounts: "アカウント"
diff --git a/package.json b/package.json
index 22cb43cb30..e5946f04f5 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
 	"name": "misskey",
 	"author": "syuilo <i@syuilo.com>",
-	"version": "11.21.0",
+	"version": "11.22.0",
 	"codename": "daybreak",
 	"repository": {
 		"type": "git",
@@ -214,7 +214,7 @@
 		"style-loader": "0.23.1",
 		"stylus": "0.54.5",
 		"stylus-loader": "3.0.2",
-		"summaly": "2.2.0",
+		"summaly": "2.3.0",
 		"systeminformation": "4.11.1",
 		"syuilo-password-strength": "0.0.1",
 		"terser-webpack-plugin": "1.3.0",
diff --git a/src/client/app/admin/views/dashboard.vue b/src/client/app/admin/views/dashboard.vue
index 639ec190e2..15bd853c14 100644
--- a/src/client/app/admin/views/dashboard.vue
+++ b/src/client/app/admin/views/dashboard.vue
@@ -124,7 +124,7 @@ export default Vue.extend({
 		this.connection = this.$root.stream.useSharedConnection('serverStats');
 
 		this.updateStats();
-		this.clock = setInterval(this.updateStats, 1000);
+		this.clock = setInterval(this.updateStats, 3000);
 
 		this.$root.getMeta().then(meta => {
 			this.meta = meta;
diff --git a/src/client/app/admin/views/db.vue b/src/client/app/admin/views/db.vue
new file mode 100644
index 0000000000..7818546e76
--- /dev/null
+++ b/src/client/app/admin/views/db.vue
@@ -0,0 +1,39 @@
+<template>
+<div>
+	<ui-card>
+		<template #title><fa :icon="faDatabase"/> {{ $t('tables') }}</template>
+		<section v-if="tables">
+			<div v-for="table in Object.keys(tables)"><b>{{ table }}</b> {{ tables[table].count | number }} {{ tables[table].size | bytes }}</div>
+		</section>
+	</ui-card>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+import i18n from '../../i18n';
+import { faDatabase } from '@fortawesome/free-solid-svg-icons';
+
+export default Vue.extend({
+	i18n: i18n('admin/views/db.vue'),
+
+	data() {
+		return {
+			tables: null,
+			faDatabase
+		};
+	},
+
+	mounted() {
+		this.fetch();
+	},
+
+	methods: {
+		fetch() {
+			this.$root.api('admin/get-table-stats').then(tables => {
+				this.tables = tables;
+			});
+		},
+	}
+});
+</script>
diff --git a/src/client/app/admin/views/index.vue b/src/client/app/admin/views/index.vue
index 43e47038f3..8a13fe1bf6 100644
--- a/src/client/app/admin/views/index.vue
+++ b/src/client/app/admin/views/index.vue
@@ -22,6 +22,7 @@
 			<li @click="nav('instance')" :class="{ active: page == 'instance' }"><fa icon="cog" fixed-width/>{{ $t('instance') }}</li>
 			<li @click="nav('queue')" :class="{ active: page == 'queue' }"><fa :icon="faTasks" fixed-width/>{{ $t('queue') }}</li>
 			<li @click="nav('logs')" :class="{ active: page == 'logs' }"><fa :icon="faStream" fixed-width/>{{ $t('logs') }}</li>
+			<li @click="nav('db')" :class="{ active: page == 'db' }"><fa :icon="faDatabase" fixed-width/>{{ $t('db') }}</li>
 			<li @click="nav('moderators')" :class="{ active: page == 'moderators' }"><fa :icon="faHeadset" fixed-width/>{{ $t('moderators') }}</li>
 			<li @click="nav('users')" :class="{ active: page == 'users' }"><fa icon="users" fixed-width/>{{ $t('users') }}</li>
 			<li @click="nav('drive')" :class="{ active: page == 'drive' }"><fa icon="cloud" fixed-width/>{{ $t('@.drive') }}</li>
@@ -43,6 +44,7 @@
 			<div v-if="page == 'instance'"><x-instance/></div>
 			<div v-if="page == 'queue'"><x-queue/></div>
 			<div v-if="page == 'logs'"><x-logs/></div>
+			<div v-if="page == 'db'"><x-db/></div>
 			<div v-if="page == 'moderators'"><x-moderators/></div>
 			<div v-if="page == 'users'"><x-users/></div>
 			<div v-if="page == 'emoji'"><x-emoji/></div>
@@ -59,19 +61,20 @@
 import Vue from 'vue';
 import i18n from '../../i18n';
 import { version } from '../../config';
-import XDashboard from "./dashboard.vue";
-import XInstance from "./instance.vue";
-import XQueue from "./queue.vue";
-import XLogs from "./logs.vue";
-import XModerators from "./moderators.vue";
-import XEmoji from "./emoji.vue";
-import XAnnouncements from "./announcements.vue";
-import XUsers from "./users.vue";
-import XDrive from "./drive.vue";
-import XAbuse from "./abuse.vue";
-import XFederation from "./federation.vue";
+import XDashboard from './dashboard.vue';
+import XInstance from './instance.vue';
+import XQueue from './queue.vue';
+import XLogs from './logs.vue';
+import XDb from './db.vue';
+import XModerators from './moderators.vue';
+import XEmoji from './emoji.vue';
+import XAnnouncements from './announcements.vue';
+import XUsers from './users.vue';
+import XDrive from './drive.vue';
+import XAbuse from './abuse.vue';
+import XFederation from './federation.vue';
 
-import { faHeadset, faArrowLeft, faGlobe, faExclamationCircle, faTasks, faStream } from '@fortawesome/free-solid-svg-icons';
+import { faHeadset, faArrowLeft, faGlobe, faExclamationCircle, faTasks, faStream, faDatabase } from '@fortawesome/free-solid-svg-icons';
 import { faGrin } from '@fortawesome/free-regular-svg-icons';
 
 // Detect the user agent
@@ -85,6 +88,7 @@ export default Vue.extend({
 		XInstance,
 		XQueue,
 		XLogs,
+		XDb,
 		XModerators,
 		XEmoji,
 		XAnnouncements,
@@ -108,7 +112,8 @@ export default Vue.extend({
 			faGlobe,
 			faExclamationCircle,
 			faTasks,
-			faStream
+			faStream,
+			faDatabase,
 		};
 	},
 	methods: {
diff --git a/src/client/app/common/views/components/poll-editor.vue b/src/client/app/common/views/components/poll-editor.vue
index ed1d02aa2c..f7a4d3af8c 100644
--- a/src/client/app/common/views/components/poll-editor.vue
+++ b/src/client/app/common/views/components/poll-editor.vue
@@ -89,9 +89,7 @@ export default Vue.extend({
 
 		get() {
 			const at = () => {
-				const [date] = moment(this.atDate).toISOString().split('T');
-				const [hour, minute] = this.atTime.split(':');
-				return moment(`${date}T${hour}:${minute}Z`).valueOf();
+				return moment(`${this.atDate} ${this.atTime}`).valueOf();
 			};
 
 			const after = () => {
diff --git a/src/client/app/common/views/widgets/server.info.vue b/src/client/app/common/views/widgets/server.info.vue
index a97b4ec496..41ccd23bfe 100644
--- a/src/client/app/common/views/widgets/server.info.vue
+++ b/src/client/app/common/views/widgets/server.info.vue
@@ -3,6 +3,7 @@
 	<p>Maintainer: <b><a :href="'mailto:' + meta.maintainerEmail" target="_blank">{{ meta.maintainerName }}</a></b></p>
 	<p>Machine: {{ meta.machine }}</p>
 	<p>Node: {{ meta.node }}</p>
+	<p>PSQL: {{ meta.psql }}</p>
 	<p>Version: {{ meta.version }} </p>
 </div>
 </template>
diff --git a/src/config/load.ts b/src/config/load.ts
index 26b25eab4e..aeed54d74e 100644
--- a/src/config/load.ts
+++ b/src/config/load.ts
@@ -3,7 +3,6 @@
  */
 
 import * as fs from 'fs';
-import { URL } from 'url';
 import * as yaml from 'js-yaml';
 import { Source, Mixin } from './types';
 import * as pkg from '../../package.json';
diff --git a/src/mfm/fromHtml.ts b/src/mfm/fromHtml.ts
index 5fc4a16416..60293b07f7 100644
--- a/src/mfm/fromHtml.ts
+++ b/src/mfm/fromHtml.ts
@@ -1,5 +1,4 @@
 import { parseFragment, DefaultTreeDocumentFragment } from 'parse5';
-import { URL } from 'url';
 import { urlRegex } from './prelude';
 
 export function fromHtml(html: string): string {
diff --git a/src/mfm/language.ts b/src/mfm/language.ts
index 003ae348a4..4750ea3380 100644
--- a/src/mfm/language.ts
+++ b/src/mfm/language.ts
@@ -98,13 +98,13 @@ export const mfmLanguage = P.createLanguage({
 			const text = input.substr(i);
 			const match = text.match(/^(\*|_)([a-zA-Z0-9]+?[\s\S]*?)\1/);
 			if (!match) return P.makeFailure(i, 'not a italic');
-			if (input[i - 1] != null && input[i - 1].match(/[a-z0-9]/i)) return P.makeFailure(i, 'not a italic');
+			if (input[i - 1] != null && input[i - 1] != ' ' && input[i - 1] != '\n') return P.makeFailure(i, 'not a italic');
 			return P.makeSuccess(i + match[0].length, match[2]);
 		});
 
 		return P.alt(xml, underscore).map(x => createTree('italic', r.inline.atLeast(1).tryParse(x), {}));
 	},
-	strike: r => P.regexp(/~~(.+?)~~/, 1).map(x => createTree('strike', r.inline.atLeast(1).tryParse(x), {})),
+	strike: r => P.regexp(/~~([^\n~]+?)~~/, 1).map(x => createTree('strike', r.inline.atLeast(1).tryParse(x), {})),
 	motion: r => {
 		const paren = P.regexp(/\(\(\(([\s\S]+?)\)\)\)/, 1);
 		const xml = P.regexp(/<motion>(.+?)<\/motion>/, 1);
@@ -164,8 +164,10 @@ export const mfmLanguage = P.createLanguage({
 			} else
 				url = match[0];
 			url = removeOrphanedBrackets(url);
-			if (url.endsWith('.')) url = url.substr(0, url.lastIndexOf('.'));
-			if (url.endsWith(',')) url = url.substr(0, url.lastIndexOf(','));
+			while (url.endsWith('.') || url.endsWith(',')) {
+				if (url.endsWith('.')) url = url.substr(0, url.lastIndexOf('.'));
+				if (url.endsWith(',')) url = url.substr(0, url.lastIndexOf(','));
+			}
 			return P.makeSuccess(i + url.length, url);
 		}).map(x => createLeaf('url', { url: x }));
 	},
diff --git a/src/misc/convert-host.ts b/src/misc/convert-host.ts
index a5fb15c66f..ad52e12588 100644
--- a/src/misc/convert-host.ts
+++ b/src/misc/convert-host.ts
@@ -1,6 +1,5 @@
 import config from '../config';
 import { toASCII } from 'punycode';
-import { URL } from 'url';
 
 export function getFullApAccount(username: string, host: string | null) {
 	return host ? `${username}@${toPuny(host)}` : `${username}@${toPuny(config.host)}`;
diff --git a/src/misc/donwload-url.ts b/src/misc/donwload-url.ts
index 167e01fdd1..e2f10c0e40 100644
--- a/src/misc/donwload-url.ts
+++ b/src/misc/donwload-url.ts
@@ -25,10 +25,8 @@ export async function downloadUrl(url: string, path: string) {
 			rej(error);
 		});
 
-		const requestUrl = new URL(url).pathname.match(/[^\u0021-\u00ff]/) ? encodeURI(url) : url;
-
 		const req = request({
-			url: requestUrl,
+			url: new URL(url).href, // https://github.com/syuilo/misskey/issues/2637
 			proxy: config.proxy,
 			timeout: 10 * 1000,
 			headers: {
diff --git a/src/queue/processors/inbox.ts b/src/queue/processors/inbox.ts
index 21a51c962b..e71181ee73 100644
--- a/src/queue/processors/inbox.ts
+++ b/src/queue/processors/inbox.ts
@@ -3,7 +3,6 @@ import * as httpSignature from 'http-signature';
 import { IRemoteUser } from '../../models/entities/user';
 import perform from '../../remote/activitypub/perform';
 import { resolvePerson, updatePerson } from '../../remote/activitypub/models/person';
-import { URL } from 'url';
 import { publishApLogStream } from '../../services/stream';
 import Logger from '../../services/logger';
 import { registerOrFetchInstanceDoc } from '../../services/register-or-fetch-instance-doc';
diff --git a/src/remote/activitypub/models/person.ts b/src/remote/activitypub/models/person.ts
index dcd64e0cd7..bfcad100fe 100644
--- a/src/remote/activitypub/models/person.ts
+++ b/src/remote/activitypub/models/person.ts
@@ -6,7 +6,6 @@ import { resolveImage } from './image';
 import { isCollectionOrOrderedCollection, isCollection, IPerson } from '../type';
 import { DriveFile } from '../../../models/entities/drive-file';
 import { fromHtml } from '../../../mfm/fromHtml';
-import { URL } from 'url';
 import { resolveNote, extractEmojis } from './note';
 import { registerOrFetchInstanceDoc } from '../../../services/register-or-fetch-instance-doc';
 import { ITag, extractHashtags } from './tag';
diff --git a/src/remote/activitypub/request.ts b/src/remote/activitypub/request.ts
index bde4921c3a..6d18e53281 100644
--- a/src/remote/activitypub/request.ts
+++ b/src/remote/activitypub/request.ts
@@ -1,6 +1,5 @@
 import { request } from 'https';
 import { sign } from 'http-signature';
-import { URL } from 'url';
 import * as crypto from 'crypto';
 import { lookup, IRunOptions } from 'lookup-dns-cache';
 import * as promiseAny from 'promise-any';
diff --git a/src/remote/resolve-user.ts b/src/remote/resolve-user.ts
index 0e2b88f05b..5253d684de 100644
--- a/src/remote/resolve-user.ts
+++ b/src/remote/resolve-user.ts
@@ -1,7 +1,6 @@
 import webFinger from './webfinger';
 import config from '../config';
 import { createPerson, updatePerson } from './activitypub/models/person';
-import { URL } from 'url';
 import { remoteLogger } from './logger';
 import chalk from 'chalk';
 import { User, IRemoteUser } from '../models/entities/user';
diff --git a/src/remote/webfinger.ts b/src/remote/webfinger.ts
index 800673943b..101a31aabb 100644
--- a/src/remote/webfinger.ts
+++ b/src/remote/webfinger.ts
@@ -1,6 +1,5 @@
 import config from '../config';
 import * as request from 'request-promise-native';
-import { URL } from 'url';
 import { query as urlQuery } from '../prelude/url';
 
 type ILink = {
diff --git a/src/server/api/endpoints/admin/get-table-stats.ts b/src/server/api/endpoints/admin/get-table-stats.ts
new file mode 100644
index 0000000000..1abea18492
--- /dev/null
+++ b/src/server/api/endpoints/admin/get-table-stats.ts
@@ -0,0 +1,37 @@
+import define from '../../define';
+import { getConnection } from 'typeorm';
+
+export const meta = {
+	requireCredential: false,
+
+	desc: {
+		'en-US': 'Get table stats'
+	},
+
+	tags: ['meta'],
+
+	params: {
+	},
+};
+
+export default define(meta, async () => {
+	const sizes = await
+		getConnection().query(`
+			SELECT relname AS "table", reltuples as "count", pg_total_relation_size(C.oid) AS "size"
+			FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
+			WHERE nspname NOT IN ('pg_catalog', 'information_schema')
+				AND C.relkind <> 'i'
+				AND nspname !~ '^pg_toast';`)
+		.then(recs => {
+			const res = {} as Record<string, { count: number; size: number; }>;
+			for (const rec of recs) {
+				res[rec.table] = {
+					count: parseInt(rec.count, 10),
+					size: parseInt(rec.size, 10),
+				};
+			}
+			return res;
+		});
+
+	return sizes;
+});
diff --git a/src/server/api/endpoints/i/update-email.ts b/src/server/api/endpoints/i/update-email.ts
index 56284499d3..ca95e612a3 100644
--- a/src/server/api/endpoints/i/update-email.ts
+++ b/src/server/api/endpoints/i/update-email.ts
@@ -8,6 +8,7 @@ import * as bcrypt from 'bcryptjs';
 import { Users, UserProfiles } from '../../../../models';
 import { ensure } from '../../../../prelude/ensure';
 import { sendEmail } from '../../../../services/send-email';
+import { ApiError } from '../../error';
 
 export const meta = {
 	requireCredential: true,
@@ -27,6 +28,14 @@ export const meta = {
 		email: {
 			validator: $.optional.nullable.str
 		},
+	},
+
+	errors: {
+		incorrectPassword: {
+			message: 'Incorrect password.',
+			code: 'INCORRECT_PASSWORD',
+			id: 'e54c1d7e-e7d6-4103-86b6-0a95069b4ad3'
+		},
 	}
 };
 
@@ -37,7 +46,7 @@ export default define(meta, async (ps, user) => {
 	const same = await bcrypt.compare(ps.password, profile.password!);
 
 	if (!same) {
-		throw new Error('incorrect password');
+		throw new ApiError(meta.errors.incorrectPassword);
 	}
 
 	await UserProfiles.update({ userId: user.id }, {
diff --git a/src/server/api/endpoints/meta.ts b/src/server/api/endpoints/meta.ts
index d18543f56a..4da0c7476c 100644
--- a/src/server/api/endpoints/meta.ts
+++ b/src/server/api/endpoints/meta.ts
@@ -6,6 +6,7 @@ import { fetchMeta } from '../../../misc/fetch-meta';
 import * as pkg from '../../../../package.json';
 import { Emojis } from '../../../models';
 import { types, bool } from '../../../misc/schema';
+import { getConnection } from 'typeorm';
 
 export const meta = {
 	stability: 'stable',
@@ -114,6 +115,7 @@ export default define(meta, async (ps, me) => {
 		machine: os.hostname(),
 		os: os.platform(),
 		node: process.version,
+		psql: await getConnection().query('SHOW server_version').then(x => x[0].server_version),
 
 		cpu: {
 			model: os.cpus()[0].model,
diff --git a/src/services/chart/core.ts b/src/services/chart/core.ts
index 6a69a21b7e..6564ef7813 100644
--- a/src/services/chart/core.ts
+++ b/src/services/chart/core.ts
@@ -163,6 +163,19 @@ export default abstract class Chart<T extends Record<string, any>> {
 				},
 				...Chart.convertSchemaToFlatColumnDefinitions(schema)
 			},
+			indices: [{
+				columns: ['date']
+			}, {
+				columns: ['span']
+			}, {
+				columns: ['group']
+			}, {
+				columns: ['span', 'date']
+			}, {
+				columns: ['date', 'group']
+			}, {
+				columns: ['span', 'date', 'group']
+			}]
 		});
 	}
 
diff --git a/test/mfm.ts b/test/mfm.ts
index 89b414eba8..be8b65264a 100644
--- a/test/mfm.ts
+++ b/test/mfm.ts
@@ -804,6 +804,14 @@ describe('MFM', () => {
 				]);
 			});
 
+			it('ignore trailing periods', () => {
+				const tokens = parse('https://example.com...');
+				assert.deepStrictEqual(tokens, [
+					leaf('url', { url: 'https://example.com' }),
+					text('...')
+				]);
+			});
+
 			it('with comma', () => {
 				const tokens = parse('https://example.com/foo?bar=a,b');
 				assert.deepStrictEqual(tokens, [
@@ -1116,6 +1124,14 @@ describe('MFM', () => {
 					], {}),
 				]);
 			});
+
+			// https://misskey.io/notes/7u1kv5dmia
+			it('ignore internal tilde', () => {
+				const tokens = parse('~~~~~');
+				assert.deepStrictEqual(tokens, [
+					text('~~~~~')
+				]);
+			});
 		});
 
 		describe('italic', () => {
@@ -1173,6 +1189,24 @@ describe('MFM', () => {
 					text('foo_bar_baz'),
 				]);
 			});
+
+			it('require spaces', () => {
+				const tokens = parse('4日目_L38b a_b');
+				assert.deepStrictEqual(tokens, [
+					text('4日目_L38b a_b'),
+				]);
+			});
+
+			it('newline sandwich', () => {
+				const tokens = parse('foo\n_bar_\nbaz');
+				assert.deepStrictEqual(tokens, [
+					text('foo\n'),
+					tree('italic', [
+						text('bar')
+					], {}),
+					text('\nbaz'),
+				]);
+			});
 		});
 	});