Add stats on Sequences/Functions/Trigger functions collection nodes. Fixes #1299
parent
6b95d6ac75
commit
71ec9ef914
|
@ -203,7 +203,7 @@ class FunctionView(PGChildNodeView, DataTypeReader):
|
||||||
'nodes': [{'get': 'node'}, {'get': 'nodes'}],
|
'nodes': [{'get': 'node'}, {'get': 'nodes'}],
|
||||||
'sql': [{'get': 'sql'}],
|
'sql': [{'get': 'sql'}],
|
||||||
'msql': [{'get': 'msql'}, {'get': 'msql'}],
|
'msql': [{'get': 'msql'}, {'get': 'msql'}],
|
||||||
'stats': [{'get': 'statistics'}],
|
'stats': [{'get': 'statistics'}, {'get': 'statistics'}],
|
||||||
'dependency': [{'get': 'dependencies'}],
|
'dependency': [{'get': 'dependencies'}],
|
||||||
'dependent': [{'get': 'dependents'}],
|
'dependent': [{'get': 'dependents'}],
|
||||||
'module.js': [{}, {}, {'get': 'module_js'}],
|
'module.js': [{}, {}, {'get': 'module_js'}],
|
||||||
|
@ -1296,7 +1296,7 @@ It may have been removed by another user or moved to another schema.
|
||||||
return ajax_response(response=sql)
|
return ajax_response(response=sql)
|
||||||
|
|
||||||
@check_precondition
|
@check_precondition
|
||||||
def statistics(self, gid, sid, did, scid, fnid):
|
def statistics(self, gid, sid, did, scid, fnid=None):
|
||||||
"""
|
"""
|
||||||
Statistics
|
Statistics
|
||||||
|
|
||||||
|
@ -1309,10 +1309,27 @@ It may have been removed by another user or moved to another schema.
|
||||||
|
|
||||||
Returns the statistics for a particular object if fnid is specified
|
Returns the statistics for a particular object if fnid is specified
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if fnid is not None:
|
||||||
|
sql = 'stats.sql'
|
||||||
|
schema_name = None
|
||||||
|
else:
|
||||||
|
sql = 'coll_stats.sql'
|
||||||
|
# Get schema name
|
||||||
|
status, schema_name = self.conn.execute_scalar(
|
||||||
|
render_template(
|
||||||
|
'schema/pg/9.1_plus/sql/get_name.sql',
|
||||||
|
scid=scid
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=schema_name)
|
||||||
|
|
||||||
status, res = self.conn.execute_dict(
|
status, res = self.conn.execute_dict(
|
||||||
render_template(
|
render_template(
|
||||||
"/".join([self.sql_template_path, 'stats.sql']),
|
"/".join([self.sql_template_path, sql]),
|
||||||
conn=self.conn, fnid=fnid
|
conn=self.conn, fnid=fnid,
|
||||||
|
scid=scid, schema_name=schema_name
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,8 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
|
||||||
node: 'function',
|
node: 'function',
|
||||||
label: '{{ _('Functions') }}',
|
label: '{{ _('Functions') }}',
|
||||||
type: 'coll-function',
|
type: 'coll-function',
|
||||||
columns: ['name', 'funcowner', 'description']
|
columns: ['name', 'funcowner', 'description'],
|
||||||
|
hasStatistics: true
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
SELECT
|
||||||
|
funcname AS {{ conn|qtIdent(_('Name')) }},
|
||||||
|
calls AS {{ conn|qtIdent(_('Number of calls')) }},
|
||||||
|
total_time AS {{ conn|qtIdent(_('Total time')) }},
|
||||||
|
self_time AS {{ conn|qtIdent(_('Self time')) }}
|
||||||
|
FROM
|
||||||
|
pg_stat_user_functions
|
||||||
|
WHERE
|
||||||
|
schemaname = {{schema_name|qtLiteral}}
|
||||||
|
AND funcid IN (
|
||||||
|
SELECT p.oid
|
||||||
|
FROM
|
||||||
|
pg_proc p
|
||||||
|
JOIN
|
||||||
|
pg_type typ ON typ.oid=p.prorettype
|
||||||
|
WHERE
|
||||||
|
p.proisagg = FALSE
|
||||||
|
AND typname NOT IN ('trigger', 'event_trigger')
|
||||||
|
)
|
||||||
|
ORDER BY funcname;
|
|
@ -0,0 +1,20 @@
|
||||||
|
SELECT
|
||||||
|
funcname AS {{ conn|qtIdent(_('Name')) }},
|
||||||
|
calls AS {{ conn|qtIdent(_('Number of calls')) }},
|
||||||
|
total_time AS {{ conn|qtIdent(_('Total time')) }},
|
||||||
|
self_time AS {{ conn|qtIdent(_('Self time')) }}
|
||||||
|
FROM
|
||||||
|
pg_stat_user_functions
|
||||||
|
WHERE
|
||||||
|
schemaname = {{schema_name|qtLiteral}}
|
||||||
|
AND funcid IN (
|
||||||
|
SELECT p.oid
|
||||||
|
FROM
|
||||||
|
pg_proc p
|
||||||
|
JOIN
|
||||||
|
pg_type typ ON typ.oid=p.prorettype
|
||||||
|
WHERE
|
||||||
|
p.proisagg = FALSE
|
||||||
|
AND typname NOT IN ('trigger', 'event_trigger')
|
||||||
|
)
|
||||||
|
ORDER BY funcname;
|
|
@ -0,0 +1,20 @@
|
||||||
|
SELECT
|
||||||
|
funcname AS {{ conn|qtIdent(_('Name')) }},
|
||||||
|
calls AS {{ conn|qtIdent(_('Number of calls')) }},
|
||||||
|
total_time AS {{ conn|qtIdent(_('Total time')) }},
|
||||||
|
self_time AS {{ conn|qtIdent(_('Self time')) }}
|
||||||
|
FROM
|
||||||
|
pg_stat_user_functions
|
||||||
|
WHERE
|
||||||
|
schemaname = {{schema_name|qtLiteral}}
|
||||||
|
AND funcid IN (
|
||||||
|
SELECT p.oid
|
||||||
|
FROM
|
||||||
|
pg_proc p
|
||||||
|
JOIN
|
||||||
|
pg_type typ ON typ.oid=p.prorettype
|
||||||
|
WHERE
|
||||||
|
p.proisagg = FALSE
|
||||||
|
AND typname NOT IN ('trigger', 'event_trigger')
|
||||||
|
)
|
||||||
|
ORDER BY funcname;
|
|
@ -0,0 +1,20 @@
|
||||||
|
SELECT
|
||||||
|
funcname AS {{ conn|qtIdent(_('Name')) }},
|
||||||
|
calls AS {{ conn|qtIdent(_('Number of calls')) }},
|
||||||
|
total_time AS {{ conn|qtIdent(_('Total time')) }},
|
||||||
|
self_time AS {{ conn|qtIdent(_('Self time')) }}
|
||||||
|
FROM
|
||||||
|
pg_stat_user_functions
|
||||||
|
WHERE
|
||||||
|
schemaname = {{schema_name|qtLiteral}}
|
||||||
|
AND funcid IN (
|
||||||
|
SELECT p.oid
|
||||||
|
FROM
|
||||||
|
pg_proc p
|
||||||
|
JOIN
|
||||||
|
pg_type typ ON typ.oid=p.prorettype
|
||||||
|
WHERE
|
||||||
|
p.proisagg = FALSE
|
||||||
|
AND typname NOT IN ('trigger', 'event_trigger')
|
||||||
|
)
|
||||||
|
ORDER BY funcname;
|
|
@ -0,0 +1,20 @@
|
||||||
|
SELECT
|
||||||
|
funcname AS {{ conn|qtIdent(_('Name')) }},
|
||||||
|
calls AS {{ conn|qtIdent(_('Number of calls')) }},
|
||||||
|
total_time AS {{ conn|qtIdent(_('Total time')) }},
|
||||||
|
self_time AS {{ conn|qtIdent(_('Self time')) }}
|
||||||
|
FROM
|
||||||
|
pg_stat_user_functions
|
||||||
|
WHERE
|
||||||
|
schemaname = {{schema_name|qtLiteral}}
|
||||||
|
AND funcid IN (
|
||||||
|
SELECT p.oid
|
||||||
|
FROM
|
||||||
|
pg_proc p
|
||||||
|
JOIN
|
||||||
|
pg_type typ ON typ.oid=p.prorettype
|
||||||
|
WHERE
|
||||||
|
p.proisagg = FALSE
|
||||||
|
AND typname NOT IN ('trigger', 'event_trigger')
|
||||||
|
)
|
||||||
|
ORDER BY funcname;
|
|
@ -0,0 +1,20 @@
|
||||||
|
SELECT
|
||||||
|
funcname AS {{ conn|qtIdent(_('Name')) }},
|
||||||
|
calls AS {{ conn|qtIdent(_('Number of calls')) }},
|
||||||
|
total_time AS {{ conn|qtIdent(_('Total time')) }},
|
||||||
|
self_time AS {{ conn|qtIdent(_('Self time')) }}
|
||||||
|
FROM
|
||||||
|
pg_stat_user_functions
|
||||||
|
WHERE
|
||||||
|
schemaname = {{schema_name|qtLiteral}}
|
||||||
|
AND funcid IN (
|
||||||
|
SELECT p.oid
|
||||||
|
FROM
|
||||||
|
pg_proc p
|
||||||
|
JOIN
|
||||||
|
pg_type typ ON typ.oid=p.prorettype
|
||||||
|
WHERE
|
||||||
|
p.proisagg = FALSE
|
||||||
|
AND typname NOT IN ('trigger', 'event_trigger')
|
||||||
|
)
|
||||||
|
ORDER BY funcname;
|
|
@ -12,7 +12,8 @@ function($, _, S, pgAdmin, pgBrowser, alertify, Function) {
|
||||||
node: 'procedure',
|
node: 'procedure',
|
||||||
label: '{{ _('Procedures') }}',
|
label: '{{ _('Procedures') }}',
|
||||||
type: 'coll-procedure',
|
type: 'coll-procedure',
|
||||||
columns: ['name', 'funcowner', 'description']
|
columns: ['name', 'funcowner', 'description'],
|
||||||
|
hasStatistics: true
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
SELECT
|
||||||
|
funcname AS {{ conn|qtIdent(_('Name')) }},
|
||||||
|
calls AS {{ conn|qtIdent(_('Number of calls')) }},
|
||||||
|
total_time AS {{ conn|qtIdent(_('Total time')) }},
|
||||||
|
self_time AS {{ conn|qtIdent(_('Self time')) }}
|
||||||
|
FROM
|
||||||
|
pg_stat_user_functions
|
||||||
|
WHERE
|
||||||
|
schemaname = {{schema_name|qtLiteral}}
|
||||||
|
AND funcid IN (
|
||||||
|
SELECT p.oid
|
||||||
|
FROM
|
||||||
|
pg_proc p
|
||||||
|
JOIN
|
||||||
|
pg_type typ ON typ.oid=p.prorettype
|
||||||
|
WHERE
|
||||||
|
p.proisagg = FALSE
|
||||||
|
AND typname NOT IN ('trigger', 'event_trigger')
|
||||||
|
)
|
||||||
|
ORDER BY funcname;
|
|
@ -0,0 +1,20 @@
|
||||||
|
SELECT
|
||||||
|
funcname AS {{ conn|qtIdent(_('Name')) }},
|
||||||
|
calls AS {{ conn|qtIdent(_('Number of calls')) }},
|
||||||
|
total_time AS {{ conn|qtIdent(_('Total time')) }},
|
||||||
|
self_time AS {{ conn|qtIdent(_('Self time')) }}
|
||||||
|
FROM
|
||||||
|
pg_stat_user_functions
|
||||||
|
WHERE
|
||||||
|
schemaname = {{schema_name|qtLiteral}}
|
||||||
|
AND funcid IN (
|
||||||
|
SELECT p.oid
|
||||||
|
FROM
|
||||||
|
pg_proc p
|
||||||
|
JOIN
|
||||||
|
pg_type typ ON typ.oid=p.prorettype
|
||||||
|
WHERE
|
||||||
|
p.proisagg = FALSE
|
||||||
|
AND typname NOT IN ('trigger', 'event_trigger')
|
||||||
|
)
|
||||||
|
ORDER BY funcname;
|
|
@ -0,0 +1,20 @@
|
||||||
|
SELECT
|
||||||
|
funcname AS {{ conn|qtIdent(_('Name')) }},
|
||||||
|
calls AS {{ conn|qtIdent(_('Number of calls')) }},
|
||||||
|
total_time AS {{ conn|qtIdent(_('Total time')) }},
|
||||||
|
self_time AS {{ conn|qtIdent(_('Self time')) }}
|
||||||
|
FROM
|
||||||
|
pg_stat_user_functions
|
||||||
|
WHERE
|
||||||
|
schemaname = {{schema_name|qtLiteral}}
|
||||||
|
AND funcid IN (
|
||||||
|
SELECT p.oid
|
||||||
|
FROM
|
||||||
|
pg_proc p
|
||||||
|
JOIN
|
||||||
|
pg_type typ ON typ.oid=p.prorettype
|
||||||
|
WHERE
|
||||||
|
p.proisagg = FALSE
|
||||||
|
AND typname NOT IN ('trigger', 'event_trigger')
|
||||||
|
)
|
||||||
|
ORDER BY funcname;
|
|
@ -11,7 +11,8 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
|
||||||
node: 'trigger_function',
|
node: 'trigger_function',
|
||||||
label: '{{ _('Trigger functions') }}',
|
label: '{{ _('Trigger functions') }}',
|
||||||
type: 'coll-trigger_function',
|
type: 'coll-trigger_function',
|
||||||
columns: ['name', 'funcowner', 'description']
|
columns: ['name', 'funcowner', 'description'],
|
||||||
|
hasStatistics: true
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
SELECT
|
||||||
|
funcname AS {{ conn|qtIdent(_('Name')) }},
|
||||||
|
calls AS {{ conn|qtIdent(_('Number of calls')) }},
|
||||||
|
total_time AS {{ conn|qtIdent(_('Total time')) }},
|
||||||
|
self_time AS {{ conn|qtIdent(_('Self time')) }}
|
||||||
|
FROM
|
||||||
|
pg_stat_user_functions
|
||||||
|
WHERE
|
||||||
|
schemaname = {{schema_name|qtLiteral}}
|
||||||
|
AND funcid IN (
|
||||||
|
SELECT p.oid
|
||||||
|
FROM
|
||||||
|
pg_proc p
|
||||||
|
JOIN
|
||||||
|
pg_type typ ON typ.oid=p.prorettype
|
||||||
|
WHERE
|
||||||
|
p.proisagg = FALSE
|
||||||
|
AND typname = 'trigger'
|
||||||
|
)
|
||||||
|
ORDER BY funcname;
|
|
@ -0,0 +1,20 @@
|
||||||
|
SELECT
|
||||||
|
funcname AS {{ conn|qtIdent(_('Name')) }},
|
||||||
|
calls AS {{ conn|qtIdent(_('Number of calls')) }},
|
||||||
|
total_time AS {{ conn|qtIdent(_('Total time')) }},
|
||||||
|
self_time AS {{ conn|qtIdent(_('Self time')) }}
|
||||||
|
FROM
|
||||||
|
pg_stat_user_functions
|
||||||
|
WHERE
|
||||||
|
schemaname = {{schema_name|qtLiteral}}
|
||||||
|
AND funcid IN (
|
||||||
|
SELECT p.oid
|
||||||
|
FROM
|
||||||
|
pg_proc p
|
||||||
|
JOIN
|
||||||
|
pg_type typ ON typ.oid=p.prorettype
|
||||||
|
WHERE
|
||||||
|
p.proisagg = FALSE
|
||||||
|
AND typname = 'trigger'
|
||||||
|
)
|
||||||
|
ORDER BY funcname;
|
|
@ -0,0 +1,20 @@
|
||||||
|
SELECT
|
||||||
|
funcname AS {{ conn|qtIdent(_('Name')) }},
|
||||||
|
calls AS {{ conn|qtIdent(_('Number of calls')) }},
|
||||||
|
total_time AS {{ conn|qtIdent(_('Total time')) }},
|
||||||
|
self_time AS {{ conn|qtIdent(_('Self time')) }}
|
||||||
|
FROM
|
||||||
|
pg_stat_user_functions
|
||||||
|
WHERE
|
||||||
|
schemaname = {{schema_name|qtLiteral}}
|
||||||
|
AND funcid IN (
|
||||||
|
SELECT p.oid
|
||||||
|
FROM
|
||||||
|
pg_proc p
|
||||||
|
JOIN
|
||||||
|
pg_type typ ON typ.oid=p.prorettype
|
||||||
|
WHERE
|
||||||
|
p.proisagg = FALSE
|
||||||
|
AND typname = 'trigger'
|
||||||
|
)
|
||||||
|
ORDER BY funcname;
|
|
@ -0,0 +1,20 @@
|
||||||
|
SELECT
|
||||||
|
funcname AS {{ conn|qtIdent(_('Name')) }},
|
||||||
|
calls AS {{ conn|qtIdent(_('Number of calls')) }},
|
||||||
|
total_time AS {{ conn|qtIdent(_('Total time')) }},
|
||||||
|
self_time AS {{ conn|qtIdent(_('Self time')) }}
|
||||||
|
FROM
|
||||||
|
pg_stat_user_functions
|
||||||
|
WHERE
|
||||||
|
schemaname = {{schema_name|qtLiteral}}
|
||||||
|
AND funcid IN (
|
||||||
|
SELECT p.oid
|
||||||
|
FROM
|
||||||
|
pg_proc p
|
||||||
|
JOIN
|
||||||
|
pg_type typ ON typ.oid=p.prorettype
|
||||||
|
WHERE
|
||||||
|
p.proisagg = FALSE
|
||||||
|
AND typname = 'trigger'
|
||||||
|
)
|
||||||
|
ORDER BY funcname;
|
|
@ -0,0 +1,20 @@
|
||||||
|
SELECT
|
||||||
|
funcname AS {{ conn|qtIdent(_('Name')) }},
|
||||||
|
calls AS {{ conn|qtIdent(_('Number of calls')) }},
|
||||||
|
total_time AS {{ conn|qtIdent(_('Total time')) }},
|
||||||
|
self_time AS {{ conn|qtIdent(_('Self time')) }}
|
||||||
|
FROM
|
||||||
|
pg_stat_user_functions
|
||||||
|
WHERE
|
||||||
|
schemaname = {{schema_name|qtLiteral}}
|
||||||
|
AND funcid IN (
|
||||||
|
SELECT p.oid
|
||||||
|
FROM
|
||||||
|
pg_proc p
|
||||||
|
JOIN
|
||||||
|
pg_type typ ON typ.oid=p.prorettype
|
||||||
|
WHERE
|
||||||
|
p.proisagg = FALSE
|
||||||
|
AND typname = 'trigger'
|
||||||
|
)
|
||||||
|
ORDER BY funcname;
|
|
@ -0,0 +1,20 @@
|
||||||
|
SELECT
|
||||||
|
funcname AS {{ conn|qtIdent(_('Name')) }},
|
||||||
|
calls AS {{ conn|qtIdent(_('Number of calls')) }},
|
||||||
|
total_time AS {{ conn|qtIdent(_('Total time')) }},
|
||||||
|
self_time AS {{ conn|qtIdent(_('Self time')) }}
|
||||||
|
FROM
|
||||||
|
pg_stat_user_functions
|
||||||
|
WHERE
|
||||||
|
schemaname = {{schema_name|qtLiteral}}
|
||||||
|
AND funcid IN (
|
||||||
|
SELECT p.oid
|
||||||
|
FROM
|
||||||
|
pg_proc p
|
||||||
|
JOIN
|
||||||
|
pg_type typ ON typ.oid=p.prorettype
|
||||||
|
WHERE
|
||||||
|
p.proisagg = FALSE
|
||||||
|
AND typname = 'trigger'
|
||||||
|
)
|
||||||
|
ORDER BY funcname;
|
|
@ -106,7 +106,7 @@ class SequenceView(PGChildNodeView):
|
||||||
'nodes': [{'get': 'node'}, {'get': 'nodes'}],
|
'nodes': [{'get': 'node'}, {'get': 'nodes'}],
|
||||||
'sql': [{'get': 'sql'}],
|
'sql': [{'get': 'sql'}],
|
||||||
'msql': [{'get': 'msql'}, {'get': 'msql'}],
|
'msql': [{'get': 'msql'}, {'get': 'msql'}],
|
||||||
'stats': [{'get': 'statistics'}],
|
'stats': [{'get': 'statistics'}, {'get': 'statistics'}],
|
||||||
'dependency': [{'get': 'dependencies'}],
|
'dependency': [{'get': 'dependencies'}],
|
||||||
'dependent': [{'get': 'dependents'}],
|
'dependent': [{'get': 'dependents'}],
|
||||||
'module.js': [{}, {}, {'get': 'module_js'}]
|
'module.js': [{}, {}, {'get': 'module_js'}]
|
||||||
|
@ -689,7 +689,7 @@ class SequenceView(PGChildNodeView):
|
||||||
)
|
)
|
||||||
|
|
||||||
@check_precondition(action="stats")
|
@check_precondition(action="stats")
|
||||||
def statistics(self, gid, sid, did, scid, seid):
|
def statistics(self, gid, sid, did, scid, seid=None):
|
||||||
"""
|
"""
|
||||||
Statistics
|
Statistics
|
||||||
|
|
||||||
|
@ -702,10 +702,26 @@ class SequenceView(PGChildNodeView):
|
||||||
|
|
||||||
Returns the statistics for a particular object if seid is specified
|
Returns the statistics for a particular object if seid is specified
|
||||||
"""
|
"""
|
||||||
|
if seid is not None:
|
||||||
|
sql = 'stats.sql'
|
||||||
|
schema_name = None
|
||||||
|
else:
|
||||||
|
sql = 'coll_stats.sql'
|
||||||
|
# Get schema name
|
||||||
|
status, schema_name = self.conn.execute_scalar(
|
||||||
|
render_template(
|
||||||
|
'schema/pg/9.1_plus/sql/get_name.sql',
|
||||||
|
scid=scid
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=schema_name)
|
||||||
|
|
||||||
status, res = self.conn.execute_dict(
|
status, res = self.conn.execute_dict(
|
||||||
render_template(
|
render_template(
|
||||||
"/".join([self.template_path, 'stats.sql']),
|
"/".join([self.template_path, sql]),
|
||||||
conn=self.conn, seid=seid
|
conn=self.conn, seid=seid,
|
||||||
|
schema_name=schema_name
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,8 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
|
||||||
node: 'sequence',
|
node: 'sequence',
|
||||||
label: '{{ _('Sequences') }}',
|
label: '{{ _('Sequences') }}',
|
||||||
type: 'coll-sequence',
|
type: 'coll-sequence',
|
||||||
columns: ['name', 'seqowner', 'comment']
|
columns: ['name', 'seqowner', 'comment'],
|
||||||
|
hasStatistics: true
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
SELECT
|
||||||
|
relname AS {{ conn|qtIdent(_('Name')) }},
|
||||||
|
blks_read AS {{ conn|qtIdent(_('Blocks read')) }},
|
||||||
|
blks_hit AS {{ conn|qtIdent(_('Blocks hit')) }}
|
||||||
|
FROM
|
||||||
|
pg_statio_all_sequences
|
||||||
|
WHERE
|
||||||
|
schemaname = {{ schema_name|qtLiteral }}
|
||||||
|
ORDER BY relname;
|
Loading…
Reference in New Issue