Added support to create an unnamed index. #3318

pull/6161/head
Akshay Joshi 2023-04-20 12:35:10 +05:30
parent ed941b5b95
commit d6ecc531e0
15 changed files with 137 additions and 67 deletions

View File

@ -2,7 +2,7 @@
Version 7.1 Version 7.1
*********** ***********
Release date: 2023-05-01 Release date: 2023-05-04
This release contains a number of bug fixes and new features since the release of pgAdmin 4 v7.0. This release contains a number of bug fixes and new features since the release of pgAdmin 4 v7.0.
@ -22,6 +22,7 @@ New features
| `Issue #3275 <https://github.com/pgadmin-org/pgadmin4/issues/3275>`_ - Allow on demand record count setting to be changed per user using preferences. | `Issue #3275 <https://github.com/pgadmin-org/pgadmin4/issues/3275>`_ - Allow on demand record count setting to be changed per user using preferences.
| `Issue #3316 <https://github.com/pgadmin-org/pgadmin4/issues/3316>`_ - Added support to show statistics for materialized views. | `Issue #3316 <https://github.com/pgadmin-org/pgadmin4/issues/3316>`_ - Added support to show statistics for materialized views.
| `Issue #3318 <https://github.com/pgadmin-org/pgadmin4/issues/3318>`_ - Added support to create an unnamed index.
Housekeeping Housekeeping
************ ************
@ -33,5 +34,8 @@ Bug fixes
| `Issue #5777 <https://github.com/pgadmin-org/pgadmin4/issues/5777>`_ - Fixed an issue where the browser tree state is not remembered when reopening pgAdmin. | `Issue #5777 <https://github.com/pgadmin-org/pgadmin4/issues/5777>`_ - Fixed an issue where the browser tree state is not remembered when reopening pgAdmin.
| `Issue #5820 <https://github.com/pgadmin-org/pgadmin4/issues/5820>`_ - Fixed an issue where collation was set to none if we remove it while creating partitioned table. | `Issue #5820 <https://github.com/pgadmin-org/pgadmin4/issues/5820>`_ - Fixed an issue where collation was set to none if we remove it while creating partitioned table.
| `Issue #6075 <https://github.com/pgadmin-org/pgadmin4/issues/6075>`_ - Ensure that the save button is enabled when registering a new server fails due to an API error. | `Issue #6075 <https://github.com/pgadmin-org/pgadmin4/issues/6075>`_ - Ensure that the save button is enabled when registering a new server fails due to an API error.
| `Issue #6120 <https://github.com/pgadmin-org/pgadmin4/issues/6120>`_ - Fixed error occurring while logging out from pgAdmin keeping a query tool opened.
| `Issue #6128 <https://github.com/pgadmin-org/pgadmin4/issues/6128>`_ - Fix a SQL error occurring on roles dependents SQL.
| `Issue #6130 <https://github.com/pgadmin-org/pgadmin4/issues/6130>`_ - Ensure to quote the primary key value if needed while deleting rows from the table. | `Issue #6130 <https://github.com/pgadmin-org/pgadmin4/issues/6130>`_ - Ensure to quote the primary key value if needed while deleting rows from the table.
| `Issue #6137 <https://github.com/pgadmin-org/pgadmin4/issues/6137>`_ - Fixed error occurring while dumping the servers from CLI. | `Issue #6137 <https://github.com/pgadmin-org/pgadmin4/issues/6137>`_ - Fixed error occurring while dumping the servers from CLI.
| `Issue #6138 <https://github.com/pgadmin-org/pgadmin4/issues/6138>`_ - Throw an appropriate error when a table for which View/Edit data is open, is deleted, and query is executed.

View File

@ -551,10 +551,17 @@ class IndexesView(PGChildNodeView, SchemaDiffObjectCompare):
return True, err_msg return True, err_msg
return False, '' return False, ''
def end_transaction(self, data):
"""
This function is used to end the transaction
"""
if hasattr(data, "isconcurrent") and not data['isconcurrent']:
self.conn.execute_scalar("END;")
@check_precondition @check_precondition
def create(self, gid, sid, did, scid, tid): def create(self, gid, sid, did, scid, tid):
""" """
This function will creates new the schema object This function will create the new index object
Args: Args:
gid: Server Group ID gid: Server Group ID
@ -579,7 +586,6 @@ class IndexesView(PGChildNodeView, SchemaDiffObjectCompare):
data[k] = v data[k] = v
required_args = { required_args = {
'name': 'Name',
'columns': 'Columns' 'columns': 'Columns'
} }
@ -611,8 +617,7 @@ class IndexesView(PGChildNodeView, SchemaDiffObjectCompare):
status, res = self.conn.execute_scalar(SQL) status, res = self.conn.execute_scalar(SQL)
if not status: if not status:
# End transaction. # End transaction.
if hasattr(data, "isconcurrent") and not data['isconcurrent']: self.end_transaction(data)
self.conn.execute_scalar("END;")
return internal_server_error(errormsg=res) return internal_server_error(errormsg=res)
# If user chooses concurrent index then we cannot run it along # If user chooses concurrent index then we cannot run it along
@ -625,27 +630,35 @@ class IndexesView(PGChildNodeView, SchemaDiffObjectCompare):
if SQL != '': if SQL != '':
status, res = self.conn.execute_scalar(SQL) status, res = self.conn.execute_scalar(SQL)
if not status: if not status:
if hasattr(data, "isconcurrent") and not data[ self.end_transaction(data)
'isconcurrent']:
# End transaction.
self.conn.execute_scalar("END;")
return internal_server_error(errormsg=res) return internal_server_error(errormsg=res)
# we need oid to add object in tree at browser # we need oid to add object in tree at browser
SQL = render_template( idx = 0
"/".join([self.template_path, self._OID_SQL]), if data.get('name', '') == "":
tid=tid, data=data, conn=self.conn SQL = render_template(
) "/".join([self.template_path, 'get_oid_name.sql']),
status, idx = self.conn.execute_scalar(SQL) tid=tid, conn=self.conn
if not status: )
if hasattr(data, "isconcurrent") and not data['isconcurrent']: status, res = self.conn.execute_dict(SQL)
# End transaction. if not status:
self.conn.execute_scalar("END;") self.end_transaction(data)
return internal_server_error(errormsg=tid) return internal_server_error(errormsg=tid)
if hasattr(data, "isconcurrent") and not data['isconcurrent']: if 'rows' in res and len(res['rows']) > 0:
# End transaction. data['name'] = res['rows'][0]['relname']
self.conn.execute_scalar("END;") idx = res['rows'][0]['oid']
else:
SQL = render_template(
"/".join([self.template_path, self._OID_SQL]),
tid=tid, data=data, conn=self.conn
)
status, idx = self.conn.execute_scalar(SQL)
if not status:
self.end_transaction(data)
return internal_server_error(errormsg=tid)
self.end_transaction(data)
return jsonify( return jsonify(
node=self.blueprint.generate_browser_node( node=self.blueprint.generate_browser_node(
idx, idx,
@ -655,9 +668,7 @@ class IndexesView(PGChildNodeView, SchemaDiffObjectCompare):
) )
) )
except Exception as e: except Exception as e:
if hasattr(data, "isconcurrent") and not data['isconcurrent']: self.end_transaction(data)
# End transaction.
self.conn.execute_scalar("END;")
return internal_server_error(errormsg=str(e)) return internal_server_error(errormsg=str(e))
@check_precondition @check_precondition

View File

@ -263,7 +263,7 @@ export default class IndexSchema extends BaseUISchema {
return [ return [
{ {
id: 'name', label: gettext('Name'), cell: 'string', id: 'name', label: gettext('Name'), cell: 'string',
type: 'text', noEmpty: true, type: 'text',
disabled: () => inSchema(indexSchemaObj.node_info), disabled: () => inSchema(indexSchemaObj.node_info),
},{ },{
id: 'oid', label: gettext('OID'), cell: 'string', id: 'oid', label: gettext('OID'), cell: 'string',
@ -366,8 +366,15 @@ export default class IndexSchema extends BaseUISchema {
group: gettext('Definition'), group: gettext('Definition'),
},{ },{
id: 'indisclustered', label: gettext('Clustered?'), cell: 'string', id: 'indisclustered', label: gettext('Clustered?'), cell: 'string',
type: 'switch', disabled: () => inSchema(indexSchemaObj.node_info), type: 'switch', group: gettext('Definition'), deps: ['name'],
group: gettext('Definition'), disabled: function (state) {
return isEmptyString(state.name) || inSchema(indexSchemaObj.node_info);
},
depChange: (state)=>{
if(isEmptyString(state.name)) {
return {indisclustered: false};
}
}
},{ },{
id: 'indisvalid', label: gettext('Valid?'), cell: 'string', id: 'indisvalid', label: gettext('Valid?'), cell: 'string',
type: 'switch', mode: ['properties'], type: 'switch', mode: ['properties'],
@ -429,7 +436,15 @@ export default class IndexSchema extends BaseUISchema {
},{ },{
id: 'description', label: gettext('Comment'), cell: 'string', id: 'description', label: gettext('Comment'), cell: 'string',
type: 'multiline', mode: ['properties', 'create', 'edit'], type: 'multiline', mode: ['properties', 'create', 'edit'],
disabled: () => inSchema(indexSchemaObj.node_info), deps: ['name'],
disabled: function (state) {
return isEmptyString(state.name) || inSchema(indexSchemaObj.node_info);
},
depChange: (state)=>{
if(isEmptyString(state.name)) {
return {comment: ''};
}
}
}, },
]; ];
} }

View File

@ -0,0 +1,4 @@
CREATE INDEX
ON public.test_table_for_indexes USING hash
(id)
TABLESPACE pg_default;

View File

@ -258,6 +258,33 @@
"data": { "data": {
"name": "test_table_for_indexes" "name": "test_table_for_indexes"
} }
},
{
"type": "create",
"name": "Create unnamed hash index",
"endpoint": "NODE-index.obj",
"sql_endpoint": "NODE-index.sql_id",
"msql_endpoint": "NODE-index.msql",
"data": {
"spcname": "pg_default",
"amname": "hash",
"columns": [{
"colname": "id",
"collspcname": "",
"op_class": "",
"sort_order": false,
"nulls": false,
"is_sort_nulls_applicable": false
}],
"indisunique": false,
"isconcurrent": false
},
"expected_msql_file": "create_unnamed_hash_index_msql.sql"
},
{
"type": "delete",
"name": "Drop hash index",
"endpoint": "NODE-index.delete_id"
} }
] ]
} }

View File

@ -1,5 +1,28 @@
{ {
"index_create": [ "index_create": [
{
"name": "Create unnamed index: With valid data.",
"is_positive_test": true,
"inventory_data": {},
"test_data": {
"spcname": "pg_default",
"amname": "btree",
"columns": [
{
"colname": "id",
"sort_order": false,
"nulls": false
}
]
},
"mocking_required": false,
"mock_data": {},
"expected_data": {
"status_code": 200,
"error_msg": null,
"test_result_data": {}
}
},
{ {
"name": "Create index: With valid data.", "name": "Create index: With valid data.",
"is_positive_test": true, "is_positive_test": true,
@ -108,32 +131,6 @@
"test_result_data": {} "test_result_data": {}
} }
}, },
{
"name": "Create index: With invalid data - Missing Parameter.",
"is_positive_test": false,
"inventory_data": {},
"test_data": {
"spcname": "pg_default",
"amname": "btree",
"columns": [
{
"colname": "id",
"sort_order": false,
"nulls": false
}
],
"include": [
"name"
]
},
"mocking_required": false,
"mock_data": {},
"expected_data": {
"status_code": 410,
"error_msg": "Could not find the required parameter (Name).",
"test_result_data": {}
}
},
{ {
"name": "Create index: With valid data while server is down.", "name": "Create index: With valid data while server is down.",
"is_positive_test": false, "is_positive_test": false,

View File

@ -61,11 +61,13 @@ class IndexesAddTestCase(BaseTestGenerator):
if self.is_positive_test: if self.is_positive_test:
response = indexes_utils.api_create_index(self) response = indexes_utils.api_create_index(self)
indexes_utils.assert_status_code(self, response) indexes_utils.assert_status_code(self, response)
index_response = indexes_utils.verify_index(self.server, # In case of unnamed index the below logic is not required
self.db_name, if hasattr(self, 'index_name'):
self.index_name) index_response = indexes_utils.verify_index(self.server,
self.assertIsNot(index_response, "Could not find the newly " self.db_name,
"created index.") self.index_name)
self.assertIsNot(index_response, "Could not find the newly "
"created index.")
else: else:
if self.mocking_required: if self.mocking_required:

View File

@ -184,7 +184,6 @@ def _get_create_sql(data, template_path, conn, mode, name,
:return: :return:
""" """
required_args = { required_args = {
'name': 'Name',
'columns': 'Columns' 'columns': 'Columns'
} }
for arg in required_args: for arg in required_args:

View File

@ -3,4 +3,5 @@ SELECT ct.oid,
NOT convalidated as convalidated NOT convalidated as convalidated
FROM pg_catalog.pg_constraint ct FROM pg_catalog.pg_constraint ct
WHERE contype='c' AND WHERE contype='c' AND
conrelid = {{tid}}::oid LIMIT 1; conrelid = {{tid}}::oid
ORDER BY ct.oid DESC LIMIT 1;

View File

@ -3,4 +3,5 @@ SELECT ct.conindid AS oid,
NOT convalidated AS convalidated NOT convalidated AS convalidated
FROM pg_catalog.pg_constraint ct FROM pg_catalog.pg_constraint ct
WHERE contype='x' AND WHERE contype='x' AND
conrelid = {{tid}}::oid LIMIT 1; conrelid = {{tid}}::oid
ORDER BY oid DESC LIMIT 1;

View File

@ -3,4 +3,5 @@ SELECT ct.oid,
NOT convalidated as convalidated NOT convalidated as convalidated
FROM pg_catalog.pg_constraint ct FROM pg_catalog.pg_constraint ct
WHERE contype='f' AND WHERE contype='f' AND
conrelid = {{tid}}::oid LIMIT 1; conrelid = {{tid}}::oid
ORDER BY ct.oid DESC LIMIT 1;

View File

@ -2,4 +2,5 @@ SELECT ct.conindid as oid,
ct.conname as name ct.conname as name
FROM pg_catalog.pg_constraint ct FROM pg_catalog.pg_constraint ct
WHERE contype='{{constraint_type}}' AND WHERE contype='{{constraint_type}}' AND
conrelid = {{tid}}::oid LIMIT 1; conrelid = {{tid}}::oid
ORDER BY oid DESC LIMIT 1;

View File

@ -1,4 +1,5 @@
CREATE {% if data.indisunique %}UNIQUE {% endif %}INDEX{% if add_not_exists_clause %} IF NOT EXISTS{% endif %} {% if data.isconcurrent %}CONCURRENTLY {% endif %}{{conn|qtIdent(data.name)}} CREATE{% if data.indisunique %} UNIQUE{% endif %} INDEX{% if add_not_exists_clause %} IF NOT EXISTS{% endif %}{% if data.isconcurrent %} CONCURRENTLY{% endif %}{% if data.name %} {{conn|qtIdent(data.name)}}{% endif %}
ON {{conn|qtIdent(data.schema, data.table)}} {% if data.amname %}USING {{conn|qtIdent(data.amname)}}{% endif %} ON {{conn|qtIdent(data.schema, data.table)}} {% if data.amname %}USING {{conn|qtIdent(data.amname)}}{% endif %}
{% if mode == 'create' %} {% if mode == 'create' %}

View File

@ -1,4 +1,5 @@
CREATE {% if data.indisunique %}UNIQUE {% endif %}INDEX{% if add_not_exists_clause %} IF NOT EXISTS{% endif %} {% if data.isconcurrent %}CONCURRENTLY {% endif %}{{conn|qtIdent(data.name)}} CREATE{% if data.indisunique %} UNIQUE{% endif %} INDEX{% if add_not_exists_clause %} IF NOT EXISTS{% endif %}{% if data.isconcurrent %} CONCURRENTLY{% endif %}{% if data.name %} {{conn|qtIdent(data.name)}}{% endif %}
ON {{conn|qtIdent(data.schema, data.table)}} {% if data.amname %}USING {{conn|qtIdent(data.amname)}}{% endif %} ON {{conn|qtIdent(data.schema, data.table)}} {% if data.amname %}USING {{conn|qtIdent(data.amname)}}{% endif %}
{% if mode == 'create' %} {% if mode == 'create' %}

View File

@ -0,0 +1,5 @@
SELECT cls.relname, cls.oid
FROM pg_catalog.pg_index idx
JOIN pg_catalog.pg_class cls ON cls.oid=indexrelid
WHERE indrelid = {{tid}}::OID
ORDER BY cls.oid DESC LIMIT 1;