1. Added Expression to CREATE INDEX. #2595

2. Added support for ALTER INDEX column statistics. #6375
This commit is contained in:
Anil Sahoo 2023-08-01 12:37:47 +05:30 committed by GitHub
parent 4920d25479
commit 7d7b8a71c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
56 changed files with 1312 additions and 281 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -8,7 +8,7 @@ Use the *Index* dialog to create an index on a specified table or materialized
view.
The *Index* dialog organizes the development of a index through the following
dialog tabs: *General* and *Definition*. The *SQL* tab displays the SQL code
dialog tabs: *General*, *Definition*, and *Columns*. The *SQL* tab displays the SQL code
generated by dialog selections.
.. image:: images/index_general.png
@ -76,28 +76,36 @@ Use the fields in the *Definition* tab to define the index:
expression limits the entries in the index to those rows that satisfy the
constraint.
Use the context-sensitive fields in the *Columns* panel to specify which
column(s) the index queries. Click the *Add* icon (+) to add a column:
Click the *Columns* tab to continue.
* Use the drop-down listbox in *Column* field to select the name of the column
rom the table.
.. image:: images/index_columns.png
:alt: Index dialog columns tab
:align: center
Use the fields in the *Columns* tab to specify which column(s) or expression(s)
the index queries. Use the *Is expression ?* switch to enable
expression sql input. Use the drop-down listbox next to *Column*
to select a column. Once the *Column* is selected or the *Expression* is
entered then click the *Add* icon (+) to provide details of the action on the
column/expression:
* The *Col/Exp* field is populated with the selection made in the *Column*
drop-down listbox or the *Expression* entered.
* If enabled, use the drop-down listbox to select an available *Operator class*
to specify the type of action performed on the column.
* If enabled, move the *Sort order* switch to specify the sort order:
* If enabled, use the drop-down listbox to select *Sort order*:
* Select *ASC* to specify an ascending sort order (the default);
* Select *DESC* to specify a descending sort order.
* If enabled, move the *Nulls* switch to specify the sort order of nulls:
* If enabled, use the drop-down listbox to select *Nulls*:
* Select *First* to specify nulls sort before non-nulls;
* Select *Last* to specify nulls sort after non-nulls (the default).
* Use the drop-down listbox in the *Collation* field to select a collation to
use for the index.
Use *Include columns* field to specify columns for *INCLUDE* clause of the
index. This option is available in Postgres 11 and later.
constraint. This option is available in Postgres 11 and later.
Click the *SQL* tab to continue.

View File

@ -7,7 +7,7 @@
//
//////////////////////////////////////////////////////////////
import IndexSchema, { getColumnSchema } from './index.ui';
import IndexSchema from './index.ui';
import { getNodeAjaxOptions, getNodeListByName } from 'pgbrowser/node_ajax';
import _ from 'lodash';
@ -46,7 +46,7 @@ define('pgadmin.node.index', [
hasSQL: true,
hasDepends: true,
hasStatistics: true,
width: pgBrowser.stdW.md + 'px',
width: pgBrowser.stdW.lg + 'px',
statsPrettifyFields: [gettext('Size'), gettext('Index size')],
url_jump_after_node: 'schema',
Init: function() {
@ -124,13 +124,14 @@ define('pgadmin.node.index', [
getSchema: (treeNodeInfo, itemNodeData) => {
let nodeObj = pgAdmin.Browser.Nodes['index'];
return new IndexSchema(
()=>getColumnSchema(nodeObj, treeNodeInfo, itemNodeData),
{
tablespaceList: ()=>getNodeListByName('tablespace', treeNodeInfo, itemNodeData, {}, (m)=>{
return (m.label != 'pg_global');
}),
amnameList : ()=>getNodeAjaxOptions('get_access_methods', nodeObj, treeNodeInfo, itemNodeData, {jumpAfterNode: 'schema'}),
columnList: ()=>getNodeListByName('column', treeNodeInfo, itemNodeData, {}),
collationList: ()=>getNodeAjaxOptions('get_collations', nodeObj, treeNodeInfo, itemNodeData, {jumpAfterNode: 'schema'}),
opClassList: ()=>getNodeAjaxOptions('get_op_class', nodeObj, treeNodeInfo, itemNodeData, {jumpAfterNode: 'schema'})
},
{
node_info: treeNodeInfo

View File

@ -9,65 +9,79 @@
import gettext from 'sources/gettext';
import BaseUISchema from 'sources/SchemaView/base_schema.ui';
import { getNodeAjaxOptions, getNodeListByName } from '../../../../../../../../static/js/node_ajax';
import DataGridViewWithHeaderForm from '../../../../../../../../../static/js/helpers/DataGridViewWithHeaderForm';
import _ from 'lodash';
import { isEmptyString } from 'sources/validators';
import Notify from '../../../../../../../../../static/js/helpers/Notifier';
export function getColumnSchema(nodeObj, treeNodeInfo, itemNodeData) {
return new ColumnSchema(
{
columnList: ()=>getNodeListByName('column', treeNodeInfo, itemNodeData, {}),
collationList: ()=>getNodeAjaxOptions('get_collations', nodeObj, treeNodeInfo, itemNodeData, {jumpAfterNode: 'schema'}),
opClassList: ()=>getNodeAjaxOptions('get_op_class', nodeObj, treeNodeInfo, itemNodeData, {jumpAfterNode: 'schema'})
}, {
node_info: treeNodeInfo
}
);
function inSchema(node_info) {
return node_info && 'catalog' in node_info;
}
export class ColumnSchema extends BaseUISchema {
constructor(fieldOptions = {}, nodeData = [], initValues={}) {
class IndexColHeaderSchema extends BaseUISchema {
constructor(columns) {
super({
name: null,
oid: undefined,
description: '',
is_sys_obj: false,
is_exp: true,
colname: undefined,
collspcname: undefined,
expression: undefined,
});
this.columns = columns;
}
changeColumnOptions(columns) {
this.columns = columns;
}
addDisabled(state) {
return !(state.is_exp ? state.expression : state.colname);
}
/* Data to IndexColumnSchema will be added using the header form */
getNewData(data) {
return this.indexColumnSchema.getNewData({
is_exp: data.is_exp,
colname: data.is_exp ? data.expression : data.colname,
});
}
get baseFields() {
return [{
id: 'is_exp', label: gettext('Is expression'), type:'switch', editable: false,
},{
id: 'colname', label: gettext('Column'), type: 'select', editable: false,
options: this.columns, deps: ['is_exp'],
optionsReloadBasis: this.columns?.map ? _.join(this.columns.map((c)=>c.label), ',') : null,
optionsLoaded: (res)=>this.columnOptions=res,
disabled: (state)=>state.is_exp, node: 'column',
},{
id: 'expression', label: gettext('Expression'), editable: false, deps: ['is_exp'],
type: 'sql', disabled: (state)=>!state.is_exp,
}];
}
}
class IndexColumnSchema extends BaseUISchema {
constructor(nodeData = {}) {
super({
colname: undefined,
is_exp: false,
op_class: undefined,
sort_order: false,
nulls: false,
is_sort_nulls_applicable: true,
...initValues
is_sort_nulls_applicable: false,
collspcname:undefined
});
this.fieldOptions = {
columnList: [],
collationList: [],
opClassList: [],
...fieldOptions
};
this.node_info = {
...nodeData.node_info
...nodeData
};
this.operClassOptions = [];
this.collationOptions = [];
this.op_class_types = [];
}
get idAttribute() {
return 'oid';
}
// We will check if we are under schema node & in 'create' mode
inSchemaWithModelCheck(state) {
if(this.node_info && 'schema' in this.node_info) {
// We will disable control if it's in 'edit' mode
return !this.isNew(state);
}
return true;
}
setOpClassTypes(options) {
if(!options || (_.isArray(options) && options.length == 0))
return this.op_class_types;
@ -88,37 +102,52 @@ export class ColumnSchema extends BaseUISchema {
return false;
}
setOperClassOptions(options) {
this.operClassOptions = options;
}
setCollationOptions(options) {
this.collationOptions = options;
}
getNewData(data) {
return {
...super.getNewData(data),
};
}
// We will check if we are under schema node & in 'create' mode
inSchemaWithModelCheck(state) {
if(this.node_info && 'schema' in this.node_info) {
// We will disable control if it's in 'edit' mode
return !this.isNew(state);
}
return true;
}
get baseFields() {
let columnSchemaObj = this;
let obj = this;
return [
{
id: 'colname', label: gettext('Column'),
type: 'select', cell: 'select', noEmpty: true,
disabled: () => inSchema(columnSchemaObj.node_info),
editable: function (state) {
return !columnSchemaObj.inSchemaWithModelCheck(state);
},
options: columnSchemaObj.fieldOptions.columnList,
node: 'column',
id: 'is_exp', label: '', type:'', cell: '', editable: false, width: 20,
disableResizing: true,
controlProps: {
formatter: {
fromRaw: function (rawValue) {
return rawValue ? 'E' : 'C';
},
}
}, visible: false,
},{
id: 'collspcname', label: gettext('Collation'),
type: 'select',
cell: 'select',
disabled: () => inSchema(columnSchemaObj.node_info),
editable: function (state) {
return !columnSchemaObj.inSchemaWithModelCheck(state);
},
options: columnSchemaObj.fieldOptions.collationList,
node: 'index',
url_jump_after_node: 'schema',
id: 'colname', label: gettext('Col/Exp'), type:'', editable: false,
cell:'', width: 100,
},{
id: 'op_class', label: gettext('Operator class'),
tags: true, type: 'select',
id: 'op_class', label: gettext('Operator class'), tags: true, type: 'select',
cell: () => {
return {
cell: 'select',
options: columnSchemaObj.fieldOptions.opClassList,
optionsLoaded: (options)=>{columnSchemaObj.setOpClassTypes(options);},
options: obj.operClassOptions,
optionsLoaded: (options)=>{obj.setOpClassTypes(options);},
controlProps: {
allowClear: true,
filter: (options) => {
@ -126,12 +155,12 @@ export class ColumnSchema extends BaseUISchema {
* to access method selected by user if not selected
* send btree related op_class options
*/
let amname = columnSchemaObj._top?._sessData ? columnSchemaObj._top?._sessData.amname : columnSchemaObj._top?._origData.amname;
let amname = obj._top?._sessData ? obj._top?._sessData.amname : obj._top?._origData.amname;
if(_.isUndefined(amname))
return options;
_.each(this.op_class_types, function(v, k) {
_.each(obj.op_class_types, function(v, k) {
if(amname === k) {
options = v;
}
@ -142,14 +171,13 @@ export class ColumnSchema extends BaseUISchema {
};
},
editable: function (state) {
return !columnSchemaObj.inSchemaWithModelCheck(state);
return !obj.inSchemaWithModelCheck(state);
},
node: 'index',
url_jump_after_node: 'schema',
deps: ['amname'],
},{
id: 'sort_order', label: gettext('Sort order'),
type: 'select', cell: 'select',
id: 'sort_order', label: gettext('Sort order'), type: 'select', cell: 'select',
options: [
{label: 'ASC', value: false},
{label: 'DESC', value: true},
@ -174,22 +202,38 @@ export class ColumnSchema extends BaseUISchema {
}
},
editable: function(state) {
return columnSchemaObj.isEditable(state);
return obj.isEditable(state);
},
deps: ['amname'],
},{
id: 'nulls', label: gettext('NULLs'),
editable: function(state) {
return columnSchemaObj.isEditable(state);
},
deps: ['amname', 'sort_order'],
type:'select', cell: 'select',
id: 'nulls', label: gettext('NULLs'), type:'select', cell: 'select',
options: [
{label: 'FIRST', value: true},
{label: 'LAST', value: false},
], controlProps: {allowClear: false},
width: 110, disableResizing: true,
},
editable: function(state) {
return obj.isEditable(state);
},
deps: ['amname', 'sort_order'],
},{
id: 'collspcname', label: gettext('Collation'),
type: 'select',
cell: 'select',
disabled: () => inSchema(obj.node_info),
editable: function (state) {
return !obj.inSchemaWithModelCheck(state);
},
options: obj.collationOptions,
node: 'index',
url_jump_after_node: 'schema',
},{
id: 'statistics', label: gettext('Statistics'),
type: 'int', cell: 'int', disabled: (state)=> {
return (!state.is_exp || obj.node_info.server.version < 110000);
},
min: -1, max: 10000, mode: ['edit','properties'],
}
];
}
}
@ -298,12 +342,8 @@ export class WithSchema extends BaseUISchema {
}
}
function inSchema(node_info) {
return node_info && 'catalog' in node_info;
}
export default class IndexSchema extends BaseUISchema {
constructor(columnSchema, fieldOptions = {}, nodeData = [], initValues={}) {
constructor(fieldOptions = {}, nodeData = {}, initValues={}) {
super({
name: undefined,
oid: undefined,
@ -322,12 +362,16 @@ export default class IndexSchema extends BaseUISchema {
tablespaceList: [],
amnameList: [],
columnList: [],
opClassList: [],
collationList: [],
...fieldOptions
};
this.node_info = {
...nodeData.node_info
};
this.getColumnSchema = columnSchema;
this.indexHeaderSchema = new IndexColHeaderSchema(this.fieldOptions.columnList);
this.indexColumnSchema = new IndexColumnSchema(this.node_info);
this.indexHeaderSchema.indexColumnSchema = this.indexColumnSchema;
this.withSchema = new WithSchema(this.node_info);
}
@ -335,6 +379,16 @@ export default class IndexSchema extends BaseUISchema {
return 'oid';
}
initialise() {
this.indexColumnSchema.setOperClassOptions(this.fieldOptions.opClassList);
this.indexColumnSchema.setCollationOptions(this.fieldOptions.collationList);
}
changeColumnOptions(columns) {
this.indexHeaderSchema.changeColumnOptions(columns);
this.fieldOptions.columns = columns;
}
getColumns() {
return {
type: 'select',
@ -412,16 +466,15 @@ export default class IndexSchema extends BaseUISchema {
};
},
deferredDepChange: (state, source, topState, actionObj) => {
const setColumns = (resolve)=>{
resolve(()=>{
state.columns.splice(0, state.columns.length);
state.columns.splice(0, state.columns?.length);
return {
columns: state.columns,
};
});
};
if((state.amname != actionObj.oldState.amname) && state.columns.length > 0) {
if((state.amname != actionObj?.oldState.amname) && state.columns?.length > 0) {
return new Promise((resolve)=>{
Notify.confirm(
gettext('Warning'),
@ -431,7 +484,7 @@ export default class IndexSchema extends BaseUISchema {
},
function() {
resolve(()=>{
state.amname = actionObj.oldState.amname;
state.amname = actionObj?.oldState.amname;
return {
amname: state.amname,
};
@ -443,22 +496,6 @@ export default class IndexSchema extends BaseUISchema {
return Promise.resolve(()=>{/*This is intentional (SonarQube)*/});
}
},
},
{
id: 'include', label: gettext('Include columns'),
group: gettext('Definition'),
editable: false, canDelete: true, canAdd: true, mode: ['properties'],
disabled: () => inSchema(indexSchemaObj.node_info),
readonly: function (state) {
return !indexSchemaObj.isNew(state);
},
type: () => {
return indexSchemaObj.getColumns();
},
visible: function() {
return indexSchemaObj.isVisible();
},
node:'column',
},{
type: 'nested-fieldset', label: gettext('With'), group: gettext('Definition'),
schema: this.withSchema,
@ -529,29 +566,36 @@ export default class IndexSchema extends BaseUISchema {
},
mode: ['create', 'edit'],
control: 'sql-field', visible: true, group: gettext('Definition'),
}, {
id: 'columns', label: gettext('Columns'), type: 'collection', deps: ['amname'],
group: gettext('Definition'), schema: indexSchemaObj.getColumnSchema(),
mode: ['edit', 'create', 'properties'],
canAdd: function(state) {
// We will disable it if it's in 'edit' mode
},{
id: 'columns', label: gettext('Columns/Expressions'),
group: gettext('Columns'), type: 'collection',
mode: ['create', 'edit', 'properties'],
editable: false, schema: this.indexColumnSchema,
headerSchema: this.indexHeaderSchema, headerVisible: (state)=>indexSchemaObj.isNew(state),
CustomControl: DataGridViewWithHeaderForm,
uniqueCol: ['colname'],
canAdd: false, canDelete: function(state) {
// We can't update columns of existing
return indexSchemaObj.isNew(state);
},
canEdit: false,
canDelete: function(state) {
// We will disable it if it's in 'edit' mode
return indexSchemaObj.isNew(state);
},
uniqueCol : ['colname'],
columns: ['colname', 'op_class', 'sort_order', 'nulls', 'collspcname']
}, {
}, cell: ()=>({
cell: '',
controlProps: {
formatter: {
fromRaw: (rawValue)=>{
return _.map(rawValue || [], 'colname').join(', ');
},
}
},
width: 245,
})
},{
id: 'include', label: gettext('Include columns'),
type: () => {
return indexSchemaObj.getColumns();
},
group: gettext('Definition'),
group: gettext('Columns'),
editable: false,
canDelete: true, canAdd: true, mode: ['edit', 'create'],
canDelete: true, canAdd: true, mode: ['edit', 'create', 'properties'],
disabled: () => inSchema(indexSchemaObj.node_info),
readonly: function (state) {
return !indexSchemaObj.isNew(state);
@ -590,7 +634,7 @@ export default class IndexSchema extends BaseUISchema {
// Checks if columns is empty
let cols = state.columns;
if(_.isArray(cols) && cols.length == 0){
msg = gettext('You must specify at least one column.');
msg = gettext('You must specify at least one column/expression.');
setError('columns', msg);
return true;
}

View File

@ -0,0 +1,14 @@
-- Index: Idx3_$%{}[]()&*^!@"'`\/#
-- DROP INDEX IF EXISTS public."Idx3_$%{}[]()&*^!@""'`\/#";
CREATE UNIQUE INDEX IF NOT EXISTS "Idx3_$%{}[]()&*^!@""'`\/#"
ON public.test_table_for_indexes USING btree
(id ASC NULLS LAST, lower(name) COLLATE pg_catalog."POSIX" text_pattern_ops ASC NULLS LAST)
INCLUDE(name, id)
WITH (fillfactor=10)
TABLESPACE pg_default
WHERE id < 100;
COMMENT ON INDEX public."Idx3_$%{}[]()&*^!@""'`\/#"
IS 'Test Comment';

View File

@ -0,0 +1,16 @@
-- Index: Idx1_$%{}[]()&*^!@"'`\/#
-- DROP INDEX IF EXISTS public."Idx1_$%{}[]()&*^!@""'`\/#";
CREATE UNIQUE INDEX IF NOT EXISTS "Idx1_$%{}[]()&*^!@""'`\/#"
ON public.test_table_for_indexes USING btree
(id DESC NULLS FIRST, name COLLATE pg_catalog."POSIX" text_pattern_ops DESC NULLS FIRST)
INCLUDE(name, id)
WITH (fillfactor=10)
TABLESPACE pg_default;
ALTER TABLE IF EXISTS public.test_table_for_indexes
CLUSTER ON "Idx1_$%{}[]()&*^!@""'`\/#";
COMMENT ON INDEX public."Idx1_$%{}[]()&*^!@""'`\/#"
IS 'Test Comment';

View File

@ -0,0 +1,12 @@
-- Index: Idx1_$%{}[]()&*^!@"'`\/#
-- DROP INDEX IF EXISTS public."Idx1_$%{}[]()&*^!@""'`\/#";
CREATE UNIQUE INDEX IF NOT EXISTS "Idx1_$%{}[]()&*^!@""'`\/#"
ON public.test_table_for_indexes USING btree
(id DESC NULLS FIRST, name COLLATE pg_catalog."POSIX" text_pattern_ops DESC NULLS FIRST)
INCLUDE(name, id)
TABLESPACE pg_default;
COMMENT ON INDEX public."Idx1_$%{}[]()&*^!@""'`\/#"
IS 'Test Comment';

View File

@ -0,0 +1,14 @@
-- Index: Idx_$%{}[]()&*^!@"'`\/#
-- DROP INDEX IF EXISTS public."Idx_$%{}[]()&*^!@""'`\/#";
CREATE UNIQUE INDEX IF NOT EXISTS "Idx_$%{}[]()&*^!@""'`\/#"
ON public.test_table_for_indexes USING btree
(id ASC NULLS FIRST, name COLLATE pg_catalog."POSIX" text_pattern_ops ASC NULLS FIRST)
INCLUDE(name, id)
WITH (fillfactor=10)
TABLESPACE pg_default
WHERE id < 100;
COMMENT ON INDEX public."Idx_$%{}[]()&*^!@""'`\/#"
IS 'Test Comment';

View File

@ -0,0 +1,10 @@
CREATE UNIQUE INDEX "Idx_$%{}[]()&*^!@""'`\/#"
ON public.test_table_for_indexes USING btree
(id ASC NULLS FIRST, name COLLATE pg_catalog."POSIX" text_pattern_ops ASC NULLS FIRST)
INCLUDE(name, id)
WITH (fillfactor=10)
TABLESPACE pg_default
WHERE id < 100;
COMMENT ON INDEX public."Idx_$%{}[]()&*^!@""'`\/#"
IS 'Test Comment';

View File

@ -0,0 +1,14 @@
-- Index: Idx_$%{}[]()&*^!@"'`\/#
-- DROP INDEX IF EXISTS public."Idx_$%{}[]()&*^!@""'`\/#";
CREATE UNIQUE INDEX IF NOT EXISTS "Idx_$%{}[]()&*^!@""'`\/#"
ON public.test_table_for_indexes USING btree
(id ASC NULLS LAST, name COLLATE pg_catalog."POSIX" text_pattern_ops ASC NULLS LAST)
INCLUDE(name, id)
WITH (fillfactor=10)
TABLESPACE pg_default
WHERE id < 100;
COMMENT ON INDEX public."Idx_$%{}[]()&*^!@""'`\/#"
IS 'Test Comment';

View File

@ -0,0 +1,10 @@
CREATE UNIQUE INDEX "Idx_$%{}[]()&*^!@""'`\/#"
ON public.test_table_for_indexes USING btree
(id ASC NULLS LAST, name COLLATE pg_catalog."POSIX" text_pattern_ops ASC NULLS LAST)
INCLUDE(name, id)
WITH (fillfactor=10)
TABLESPACE pg_default
WHERE id < 100;
COMMENT ON INDEX public."Idx_$%{}[]()&*^!@""'`\/#"
IS 'Test Comment';

View File

@ -0,0 +1,9 @@
-- Index: Idx_$%{}[]()&*^!@"'`\/#
-- DROP INDEX IF EXISTS public."Idx_$%{}[]()&*^!@""'`\/#";
CREATE UNIQUE INDEX IF NOT EXISTS "Idx_$%{}[]()&*^!@""'`\/#"
ON public.test_table_for_indexes USING btree
(id DESC NULLS FIRST, name COLLATE pg_catalog."POSIX" text_pattern_ops DESC NULLS FIRST)
INCLUDE(name, id)
TABLESPACE pg_default;

View File

@ -0,0 +1,5 @@
CREATE UNIQUE INDEX "Idx_$%{}[]()&*^!@""'`\/#"
ON public.test_table_for_indexes USING btree
(id DESC NULLS FIRST, name COLLATE pg_catalog."POSIX" text_pattern_ops DESC NULLS FIRST)
INCLUDE(name, id)
TABLESPACE pg_default;

View File

@ -0,0 +1,14 @@
-- Index: Idx_$%{}[]()&*^!@"'`\/#
-- DROP INDEX IF EXISTS public."Idx_$%{}[]()&*^!@""'`\/#";
CREATE UNIQUE INDEX IF NOT EXISTS "Idx_$%{}[]()&*^!@""'`\/#"
ON public.test_table_for_indexes USING btree
(id DESC NULLS LAST, name COLLATE pg_catalog."POSIX" text_pattern_ops DESC NULLS LAST)
INCLUDE(name, id)
WITH (fillfactor=10)
TABLESPACE pg_default
WHERE id < 100;
COMMENT ON INDEX public."Idx_$%{}[]()&*^!@""'`\/#"
IS 'Test Comment';

View File

@ -0,0 +1,10 @@
CREATE UNIQUE INDEX "Idx_$%{}[]()&*^!@""'`\/#"
ON public.test_table_for_indexes USING btree
(id DESC NULLS LAST, name COLLATE pg_catalog."POSIX" text_pattern_ops DESC NULLS LAST)
INCLUDE(name, id)
WITH (fillfactor=10)
TABLESPACE pg_default
WHERE id < 100;
COMMENT ON INDEX public."Idx_$%{}[]()&*^!@""'`\/#"
IS 'Test Comment';

View File

@ -0,0 +1,14 @@
-- Index: Idx3_$%{}[]()&*^!@"'`\/#
-- DROP INDEX IF EXISTS public."Idx3_$%{}[]()&*^!@""'`\/#";
CREATE UNIQUE INDEX IF NOT EXISTS "Idx3_$%{}[]()&*^!@""'`\/#"
ON public.test_table_for_indexes USING btree
(id ASC NULLS LAST, lower(name) COLLATE pg_catalog."POSIX" text_pattern_ops ASC NULLS LAST)
INCLUDE(name, id)
WITH (fillfactor=10)
TABLESPACE pg_default
WHERE id < 100;
COMMENT ON INDEX public."Idx3_$%{}[]()&*^!@""'`\/#"
IS 'Test Comment';

View File

@ -0,0 +1,10 @@
CREATE UNIQUE INDEX "Idx3_$%{}[]()&*^!@""'`\/#"
ON public.test_table_for_indexes USING btree
(id ASC NULLS LAST, (lower(name)) COLLATE pg_catalog."POSIX" text_pattern_ops ASC NULLS LAST)
INCLUDE(name, id)
WITH (fillfactor=10)
TABLESPACE pg_default
WHERE id < 100;
COMMENT ON INDEX public."Idx3_$%{}[]()&*^!@""'`\/#"
IS 'Test Comment';

View File

@ -0,0 +1,364 @@
{
"scenarios": [
{
"type": "create",
"name": "Create Table for indexes",
"endpoint": "NODE-table.obj",
"sql_endpoint": "NODE-table.sql_id",
"data": {
"name": "test_table_for_indexes",
"columns": [{
"name": "id",
"cltype": "bigint",
"is_primary_key": true
}, {
"name": "name",
"cltype": "text"
}],
"is_partitioned": false,
"spcname": "pg_default",
"schema": "public"
},
"store_object_id": true
},
{
"type": "create",
"name": "Create btree index with ASC and NULLS LAST -- 11 Plus",
"endpoint": "NODE-index.obj",
"sql_endpoint": "NODE-index.sql_id",
"msql_endpoint": "NODE-index.msql",
"data": {
"name":"Idx_$%{}[]()&*^!@\"'`\\/#",
"spcname":"pg_default",
"amname":"btree",
"include": ["name", "id"],
"columns":[{
"colname":"id",
"collspcname":"",
"op_class":"",
"sort_order":false,
"nulls":false,
"is_sort_nulls_applicable":true
}, {
"colname":"name",
"collspcname":"pg_catalog.\"POSIX\"",
"op_class":"text_pattern_ops",
"sort_order":false,
"nulls":false,
"is_sort_nulls_applicable":true
}],
"description":"Test Comment",
"fillfactor":"10",
"indisunique":true,
"indisclustered":false,
"isconcurrent":false,
"indconstraint":"id < 100"
},
"expected_sql_file": "create_btree_asc_null_last.sql",
"expected_msql_file": "create_btree_asc_null_last_msql.sql"
},
{
"type": "delete",
"name": "Drop index -- 11 Plus",
"endpoint": "NODE-index.delete_id",
"data": {
"name": "Idx_$%{}[]()&*^!@\"'`\\/#"
}
},
{
"type": "create",
"name": "Create btree index with ASC and NULLS FIRST -- 11 Plus",
"endpoint": "NODE-index.obj",
"sql_endpoint": "NODE-index.sql_id",
"msql_endpoint": "NODE-index.msql",
"data": {
"name":"Idx_$%{}[]()&*^!@\"'`\\/#",
"spcname":"pg_default",
"amname":"btree",
"include": ["name", "id"],
"columns":[{
"colname":"id",
"collspcname":"",
"op_class":"",
"sort_order":false,
"nulls":true,
"is_sort_nulls_applicable":true
}, {
"colname":"name",
"collspcname":"pg_catalog.\"POSIX\"",
"op_class":"text_pattern_ops",
"sort_order":false,
"nulls":true,
"is_sort_nulls_applicable":true
}],
"description":"Test Comment",
"fillfactor":"10",
"indisunique":true,
"indisclustered":false,
"isconcurrent":false,
"indconstraint":"id < 100"
},
"expected_sql_file": "create_btree_asc_null_first.sql",
"expected_msql_file": "create_btree_asc_null_first_msql.sql"
},
{
"type": "delete",
"name": "Drop index -- 11 Plus",
"endpoint": "NODE-index.delete_id",
"data": {
"name": "Idx_$%{}[]()&*^!@\"'`\\/#"
}
},
{
"type": "create",
"name": "Create btree index with DESC and NULLS LAST -- 11 Plus",
"endpoint": "NODE-index.obj",
"sql_endpoint": "NODE-index.sql_id",
"msql_endpoint": "NODE-index.msql",
"data": {
"name":"Idx_$%{}[]()&*^!@\"'`\\/#",
"spcname":"pg_default",
"amname":"btree",
"include": ["name", "id"],
"columns":[{
"colname":"id",
"collspcname":"",
"op_class":"",
"sort_order":true,
"nulls":false,
"is_sort_nulls_applicable":true
}, {
"colname":"name",
"collspcname":"pg_catalog.\"POSIX\"",
"op_class":"text_pattern_ops",
"sort_order":true,
"nulls":false,
"is_sort_nulls_applicable":true
}],
"description":"Test Comment",
"fillfactor":"10",
"indisunique":true,
"indisclustered":false,
"isconcurrent":false,
"indconstraint":"id < 100"
},
"expected_sql_file": "create_btree_desc_null_last.sql",
"expected_msql_file": "create_btree_desc_null_last_msql.sql"
},
{
"type": "delete",
"name": "Drop index -- 11 Plus",
"endpoint": "NODE-index.delete_id",
"data": {
"name": "Idx_$%{}[]()&*^!@\"'`\\/#"
}
},
{
"type": "create",
"name": "Create btree index with DESC and NULLS FIRST -- 11 Plus",
"endpoint": "NODE-index.obj",
"sql_endpoint": "NODE-index.sql_id",
"msql_endpoint": "NODE-index.msql",
"data": {
"name":"Idx_$%{}[]()&*^!@\"'`\\/#",
"spcname":"pg_default",
"amname":"btree",
"include": ["name", "id"],
"columns":[{
"colname":"id",
"collspcname":"",
"op_class":"",
"sort_order":true,
"nulls":true,
"is_sort_nulls_applicable":true
}, {
"colname":"name",
"collspcname":"pg_catalog.\"POSIX\"",
"op_class":"text_pattern_ops",
"sort_order":true,
"nulls":true,
"is_sort_nulls_applicable":true
}],
"indisunique":true,
"indisclustered":false,
"isconcurrent":false
},
"expected_sql_file": "create_btree_desc_null_first.sql",
"expected_msql_file": "create_btree_desc_null_first_msql.sql"
},
{
"type": "alter",
"name": "Alter index name, fill factor, comment and clustered -- 11 Plus",
"endpoint": "NODE-index.obj_id",
"sql_endpoint": "NODE-index.sql_id",
"msql_endpoint": "NODE-index.msql_id",
"data": {
"name": "Idx1_$%{}[]()&*^!@\"'`\\/#",
"description":"Test Comment",
"fillfactor":"10",
"indisclustered":true
},
"expected_sql_file": "alter_name_fillfactor_comment.sql",
"expected_msql_file": "alter_name_fillfactor_comment_msql.sql"
},
{
"type": "alter",
"name": "Alter reset fill factor and cluster -- 11 Plus",
"endpoint": "NODE-index.obj_id",
"sql_endpoint": "NODE-index.sql_id",
"msql_endpoint": "NODE-index.msql_id",
"data": {
"fillfactor": "",
"indisclustered": false
},
"expected_sql_file": "alter_reset_fillfactor_cluster.sql",
"expected_msql_file": "alter_reset_fillfactor_cluster_msql.sql"
},
{
"type": "delete",
"name": "Drop index -- 11 Plus",
"endpoint": "NODE-index.delete_id",
"data": {
"name": "Idx1_$%{}[]()&*^!@\"'`\\/#"
}
},
{
"type": "create",
"name": "Create btree index with expression and ASC and NULLS LAST -- 11 Plus",
"endpoint": "NODE-index.obj",
"sql_endpoint": "NODE-index.sql_id",
"msql_endpoint": "NODE-index.msql",
"data": {
"name":"Idx3_$%{}[]()&*^!@\"'`\\/#",
"spcname":"pg_default",
"amname":"btree",
"include": ["name", "id"],
"columns":[{
"colname":"id",
"collspcname":"",
"op_class":"",
"sort_order":false,
"nulls":false,
"is_sort_nulls_applicable":true
}, {
"is_exp": true,
"colname":"lower(name)",
"collspcname":"pg_catalog.\"POSIX\"",
"op_class":"text_pattern_ops",
"sort_order":false,
"nulls":false,
"is_sort_nulls_applicable":true
}],
"description":"Test Comment",
"fillfactor":"10",
"indisunique":true,
"indisclustered":false,
"isconcurrent":false,
"indconstraint":"id < 100"
},
"expected_sql_file": "create_btree_expr_asc_null_last.sql",
"expected_msql_file": "create_btree_expr_asc_null_last_msql.sql"
},
{
"type": "alter",
"name": "Alter index statistics of expression -- 11 Plus",
"endpoint": "NODE-index.obj_id",
"sql_endpoint": "NODE-index.sql_id",
"msql_endpoint": "NODE-index.msql_id",
"data": {
"name": "Idx3_$%{}[]()&*^!@\"'`\\/#",
"columns":{
"changed": [{
"is_exp": true,
"col_num": 2,
"colname":"lower(name)",
"collspcname":"pg_catalog.\"POSIX\"",
"op_class":"text_pattern_ops",
"sort_order":false,
"nulls":false,
"is_sort_nulls_applicable":true,
"statistics": 1000
}]
}
},
"expected_sql_file": "alter_expr_statistics.sql",
"expected_msql_file": "alter_expr_statistics_msql.sql"
},
{
"type": "delete",
"name": "Drop index -- 11 Plus",
"endpoint": "NODE-index.delete_id",
"data": {
"name": "Idx3_$%{}[]()&*^!@\"'`\\/#"
}
},
{
"type": "create",
"name": "Create hash index -- 11 Plus",
"endpoint": "NODE-index.obj",
"sql_endpoint": "NODE-index.sql_id",
"msql_endpoint": "NODE-index.msql",
"data": {
"name": "Idx_$%{}[]()&*^!@\"'`\\/#",
"spcname": "pg_default",
"amname": "hash",
"columns": [{
"colname": "id",
"collspcname": "",
"op_class": "",
"sort_order": false,
"nulls": false,
"is_sort_nulls_applicable": false
}],
"indisunique": false,
"indisclustered": false,
"isconcurrent": false
},
"expected_sql_file": "create_hash_index.sql",
"expected_msql_file": "create_hash_index_msql.sql"
},
{
"type": "delete",
"name": "Drop hash index -- 11 Plus",
"endpoint": "NODE-index.delete_id",
"data": {
"name": "Idx_$%{}[]()&*^!@\"'`\\/#"
}
},
{
"type": "delete",
"name": "Drop Table",
"endpoint": "NODE-table.delete_id",
"data": {
"name": "test_table_for_indexes"
}
},
{
"type": "create",
"name": "Create unnamed hash index -- 11 Plus",
"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 -- 11 Plus",
"endpoint": "NODE-index.delete_id"
}
]
}

View File

@ -5,6 +5,7 @@
CREATE UNIQUE INDEX IF NOT EXISTS "Idx_$%{}[]()&*^!@""'`\/#"
ON public.test_table_for_indexes USING btree
(id ASC NULLS FIRST, name COLLATE pg_catalog."POSIX" text_pattern_ops ASC NULLS FIRST)
INCLUDE(name, id)
WITH (fillfactor=10, deduplicate_items=False)
TABLESPACE pg_default
WHERE id < 100;

View File

@ -1,6 +1,7 @@
CREATE UNIQUE INDEX "Idx_$%{}[]()&*^!@""'`\/#"
ON public.test_table_for_indexes USING btree
(id ASC NULLS FIRST, name COLLATE pg_catalog."POSIX" text_pattern_ops ASC NULLS FIRST)
INCLUDE(name, id)
WITH (fillfactor=10, deduplicate_items=False)
TABLESPACE pg_default
WHERE id < 100;

View File

@ -5,6 +5,7 @@
CREATE UNIQUE INDEX IF NOT EXISTS "Idx_$%{}[]()&*^!@""'`\/#"
ON public.test_table_for_indexes USING btree
(id ASC NULLS LAST, name COLLATE pg_catalog."POSIX" text_pattern_ops ASC NULLS LAST)
INCLUDE(name, id)
WITH (fillfactor=10, deduplicate_items=False)
TABLESPACE pg_default
WHERE id < 100;

View File

@ -1,6 +1,7 @@
CREATE UNIQUE INDEX "Idx_$%{}[]()&*^!@""'`\/#"
ON public.test_table_for_indexes USING btree
(id ASC NULLS LAST, name COLLATE pg_catalog."POSIX" text_pattern_ops ASC NULLS LAST)
INCLUDE(name, id)
WITH (fillfactor=10, deduplicate_items=False)
TABLESPACE pg_default
WHERE id < 100;

View File

@ -5,6 +5,7 @@
CREATE UNIQUE INDEX IF NOT EXISTS "Idx_$%{}[]()&*^!@""'`\/#"
ON public.test_table_for_indexes USING btree
(id DESC NULLS LAST, name COLLATE pg_catalog."POSIX" text_pattern_ops DESC NULLS LAST)
INCLUDE(name, id)
WITH (fillfactor=10, deduplicate_items=False)
TABLESPACE pg_default
WHERE id < 100;

View File

@ -1,6 +1,7 @@
CREATE UNIQUE INDEX "Idx_$%{}[]()&*^!@""'`\/#"
ON public.test_table_for_indexes USING btree
(id DESC NULLS LAST, name COLLATE pg_catalog."POSIX" text_pattern_ops DESC NULLS LAST)
INCLUDE(name, id)
WITH (fillfactor=10, deduplicate_items=False)
TABLESPACE pg_default
WHERE id < 100;

View File

@ -23,7 +23,7 @@
},
{
"type": "create",
"name": "Create btree index with ASC and NULLS LAST -- 15 Plus",
"name": "Create btree index with ASC and NULLS LAST -- 13 Plus",
"endpoint": "NODE-index.obj",
"sql_endpoint": "NODE-index.sql_id",
"msql_endpoint": "NODE-index.msql",
@ -31,6 +31,7 @@
"name":"Idx_$%{}[]()&*^!@\"'`\\/#",
"spcname":"pg_default",
"amname":"btree",
"include": ["name", "id"],
"columns":[{
"colname":"id",
"collspcname":"",
@ -60,7 +61,7 @@
},
{
"type": "delete",
"name": "Drop index",
"name": "Drop index -- 13 Plus",
"endpoint": "NODE-index.delete_id",
"data": {
"name": "Idx_$%{}[]()&*^!@\"'`\\/#"
@ -76,6 +77,7 @@
"name":"Idx_$%{}[]()&*^!@\"'`\\/#",
"spcname":"pg_default",
"amname":"btree",
"include": ["name", "id"],
"columns":[{
"colname":"id",
"collspcname":"",
@ -105,7 +107,7 @@
},
{
"type": "delete",
"name": "Drop index -- 15 Plus",
"name": "Drop index -- 13 Plus",
"endpoint": "NODE-index.delete_id",
"data": {
"name": "Idx_$%{}[]()&*^!@\"'`\\/#"
@ -113,7 +115,7 @@
},
{
"type": "create",
"name": "Create btree index with DESC and NULLS LAST -- 15 Plus",
"name": "Create btree index with DESC and NULLS LAST -- 13 Plus",
"endpoint": "NODE-index.obj",
"sql_endpoint": "NODE-index.sql_id",
"msql_endpoint": "NODE-index.msql",
@ -121,6 +123,7 @@
"name":"Idx_$%{}[]()&*^!@\"'`\\/#",
"spcname":"pg_default",
"amname":"btree",
"include": ["name", "id"],
"columns":[{
"colname":"id",
"collspcname":"",
@ -150,7 +153,7 @@
},
{
"type": "delete",
"name": "Drop index -- 15 Plus",
"name": "Drop index -- 13 Plus",
"endpoint": "NODE-index.delete_id",
"data": {
"name": "Idx_$%{}[]()&*^!@\"'`\\/#"
@ -158,7 +161,7 @@
},
{
"type": "create",
"name": "Create btree index with DESC and NULLS FIRST -- 15 Plus",
"name": "Create btree index with DESC and NULLS FIRST -- 13 Plus",
"endpoint": "NODE-index.obj",
"sql_endpoint": "NODE-index.sql_id",
"msql_endpoint": "NODE-index.msql",
@ -166,6 +169,7 @@
"name":"Idx_$%{}[]()&*^!@\"'`\\/#",
"spcname":"pg_default",
"amname":"btree",
"include": ["name", "id"],
"columns":[{
"colname":"id",
"collspcname":"",
@ -191,7 +195,7 @@
},
{
"type": "alter",
"name": "Alter index name, fill factor, comment and clustered -- 15 Plus",
"name": "Alter index name, fill factor, comment and clustered -- 13 Plus",
"endpoint": "NODE-index.obj_id",
"sql_endpoint": "NODE-index.sql_id",
"msql_endpoint": "NODE-index.msql_id",
@ -206,7 +210,7 @@
},
{
"type": "alter",
"name": "Alter reset fill factor and cluster -- 15 Plus",
"name": "Alter reset fill factor and cluster -- 13 Plus",
"endpoint": "NODE-index.obj_id",
"sql_endpoint": "NODE-index.sql_id",
"msql_endpoint": "NODE-index.msql_id",
@ -219,15 +223,85 @@
},
{
"type": "delete",
"name": "Drop index -- 15 Plus",
"name": "Drop index -- 13 Plus",
"endpoint": "NODE-index.delete_id",
"data": {
"name": "Idx1_$%{}[]()&*^!@\"'`\\/#"
}
},
{
"type": "create",
"name": "Create btree index with expression and ASC and NULLS LAST -- 13 Plus",
"endpoint": "NODE-index.obj",
"sql_endpoint": "NODE-index.sql_id",
"msql_endpoint": "NODE-index.msql",
"data": {
"name":"Idx3_$%{}[]()&*^!@\"'`\\/#",
"spcname":"pg_default",
"amname":"btree",
"include": ["name", "id"],
"columns":[{
"colname":"id",
"collspcname":"",
"op_class":"",
"sort_order":false,
"nulls":false,
"is_sort_nulls_applicable":true
}, {
"is_exp": true,
"colname":"lower(name)",
"collspcname":"pg_catalog.\"POSIX\"",
"op_class":"text_pattern_ops",
"sort_order":false,
"nulls":false,
"is_sort_nulls_applicable":true
}],
"description":"Test Comment",
"fillfactor":"10",
"indisunique":true,
"indisclustered":false,
"isconcurrent":false,
"indconstraint":"id < 100"
},
"expected_sql_file": "create_btree_expr_asc_null_last.sql",
"expected_msql_file": "create_btree_expr_asc_null_last_msql.sql"
},
{
"type": "alter",
"name": "Alter index statistics of expression -- 13 Plus",
"endpoint": "NODE-index.obj_id",
"sql_endpoint": "NODE-index.sql_id",
"msql_endpoint": "NODE-index.msql_id",
"data": {
"name": "Idx3_$%{}[]()&*^!@\"'`\\/#",
"columns":{
"changed": [{
"is_exp": true,
"col_num": 2,
"colname":"lower(name)",
"collspcname":"pg_catalog.\"POSIX\"",
"op_class":"text_pattern_ops",
"sort_order":false,
"nulls":false,
"is_sort_nulls_applicable":true,
"statistics": 1000
}]
}
},
"expected_sql_file": "alter_expr_statistics.sql",
"expected_msql_file": "alter_expr_statistics_msql.sql"
},
{
"type": "delete",
"name": "Drop index -- 13 Plus",
"endpoint": "NODE-index.delete_id",
"data": {
"name": "Idx3_$%{}[]()&*^!@\"'`\\/#"
}
},
{
"type": "create",
"name": "Create hash index -- 15 Plus",
"name": "Create hash index -- 13 Plus",
"endpoint": "NODE-index.obj",
"sql_endpoint": "NODE-index.sql_id",
"msql_endpoint": "NODE-index.msql",
@ -252,7 +326,7 @@
},
{
"type": "delete",
"name": "Drop hash index -- 15 Plus",
"name": "Drop hash index -- 13 Plus",
"endpoint": "NODE-index.delete_id",
"data": {
"name": "Idx_$%{}[]()&*^!@\"'`\\/#"
@ -268,7 +342,7 @@
},
{
"type": "create",
"name": "Create unnamed hash index -- 15 Plus",
"name": "Create unnamed hash index -- 13 Plus",
"endpoint": "NODE-index.obj",
"sql_endpoint": "NODE-index.sql_id",
"msql_endpoint": "NODE-index.msql",
@ -290,9 +364,8 @@
},
{
"type": "delete",
"name": "Drop hash index -- 15 Plus",
"name": "Drop hash index -- 13 Plus",
"endpoint": "NODE-index.delete_id"
}
]
}

View File

@ -5,6 +5,7 @@
CREATE UNIQUE INDEX IF NOT EXISTS "Idx1_$%{}[]()&*^!@""'`\/#"
ON public.test_table_for_indexes USING btree
(id DESC NULLS FIRST, name COLLATE pg_catalog."POSIX" text_pattern_ops DESC NULLS FIRST)
INCLUDE(name, id)
NULLS NOT DISTINCT
WITH (fillfactor=10)
TABLESPACE pg_default;

View File

@ -5,6 +5,7 @@
CREATE UNIQUE INDEX IF NOT EXISTS "Idx1_$%{}[]()&*^!@""'`\/#"
ON public.test_table_for_indexes USING btree
(id DESC NULLS FIRST, name COLLATE pg_catalog."POSIX" text_pattern_ops DESC NULLS FIRST)
INCLUDE(name, id)
NULLS NOT DISTINCT
TABLESPACE pg_default;

View File

@ -5,6 +5,7 @@
CREATE UNIQUE INDEX IF NOT EXISTS "Idx_$%{}[]()&*^!@""'`\/#"
ON public.test_table_for_indexes USING btree
(id ASC NULLS FIRST, name COLLATE pg_catalog."POSIX" text_pattern_ops ASC NULLS FIRST)
INCLUDE(name, id)
NULLS NOT DISTINCT
WITH (fillfactor=10)
TABLESPACE pg_default

View File

@ -1,6 +1,7 @@
CREATE UNIQUE INDEX "Idx_$%{}[]()&*^!@""'`\/#"
ON public.test_table_for_indexes USING btree
(id ASC NULLS FIRST, name COLLATE pg_catalog."POSIX" text_pattern_ops ASC NULLS FIRST)
INCLUDE(name, id)
NULLS NOT DISTINCT
WITH (fillfactor=10)
TABLESPACE pg_default

View File

@ -5,6 +5,7 @@
CREATE UNIQUE INDEX IF NOT EXISTS "Idx_$%{}[]()&*^!@""'`\/#"
ON public.test_table_for_indexes USING btree
(id ASC NULLS LAST, name COLLATE pg_catalog."POSIX" text_pattern_ops ASC NULLS LAST)
INCLUDE(name, id)
NULLS NOT DISTINCT
WITH (fillfactor=10)
TABLESPACE pg_default

View File

@ -1,6 +1,7 @@
CREATE UNIQUE INDEX "Idx_$%{}[]()&*^!@""'`\/#"
ON public.test_table_for_indexes USING btree
(id ASC NULLS LAST, name COLLATE pg_catalog."POSIX" text_pattern_ops ASC NULLS LAST)
INCLUDE(name, id)
NULLS NOT DISTINCT
WITH (fillfactor=10)
TABLESPACE pg_default

View File

@ -5,5 +5,6 @@
CREATE UNIQUE INDEX IF NOT EXISTS "Idx_$%{}[]()&*^!@""'`\/#"
ON public.test_table_for_indexes USING btree
(id DESC NULLS FIRST, name COLLATE pg_catalog."POSIX" text_pattern_ops DESC NULLS FIRST)
INCLUDE(name, id)
NULLS NOT DISTINCT
TABLESPACE pg_default;

View File

@ -1,5 +1,6 @@
CREATE UNIQUE INDEX "Idx_$%{}[]()&*^!@""'`\/#"
ON public.test_table_for_indexes USING btree
(id DESC NULLS FIRST, name COLLATE pg_catalog."POSIX" text_pattern_ops DESC NULLS FIRST)
INCLUDE(name, id)
NULLS NOT DISTINCT
TABLESPACE pg_default;

View File

@ -5,6 +5,7 @@
CREATE UNIQUE INDEX IF NOT EXISTS "Idx_$%{}[]()&*^!@""'`\/#"
ON public.test_table_for_indexes USING btree
(id DESC NULLS LAST, name COLLATE pg_catalog."POSIX" text_pattern_ops DESC NULLS LAST)
INCLUDE(name, id)
NULLS NOT DISTINCT
WITH (fillfactor=10)
TABLESPACE pg_default

View File

@ -1,6 +1,7 @@
CREATE UNIQUE INDEX "Idx_$%{}[]()&*^!@""'`\/#"
ON public.test_table_for_indexes USING btree
(id DESC NULLS LAST, name COLLATE pg_catalog."POSIX" text_pattern_ops DESC NULLS LAST)
INCLUDE(name, id)
NULLS NOT DISTINCT
WITH (fillfactor=10)
TABLESPACE pg_default

View File

@ -31,6 +31,7 @@
"name":"Idx_$%{}[]()&*^!@\"'`\\/#",
"spcname":"pg_default",
"amname":"btree",
"include": ["name", "id"],
"columns":[{
"colname":"id",
"collspcname":"",
@ -75,6 +76,7 @@
"name":"Idx_$%{}[]()&*^!@\"'`\\/#",
"spcname":"pg_default",
"amname":"btree",
"include": ["name", "id"],
"columns":[{
"colname":"id",
"collspcname":"",
@ -119,6 +121,7 @@
"name":"Idx_$%{}[]()&*^!@\"'`\\/#",
"spcname":"pg_default",
"amname":"btree",
"include": ["name", "id"],
"columns":[{
"colname":"id",
"collspcname":"",
@ -163,6 +166,7 @@
"name":"Idx_$%{}[]()&*^!@\"'`\\/#",
"spcname":"pg_default",
"amname":"btree",
"include": ["name", "id"],
"columns":[{
"colname":"id",
"collspcname":"",
@ -222,6 +226,76 @@
"name": "Idx1_$%{}[]()&*^!@\"'`\\/#"
}
},
{
"type": "create",
"name": "Create btree index with expression and ASC and NULLS LAST -- 15 Plus",
"endpoint": "NODE-index.obj",
"sql_endpoint": "NODE-index.sql_id",
"msql_endpoint": "NODE-index.msql",
"data": {
"name":"Idx3_$%{}[]()&*^!@\"'`\\/#",
"spcname":"pg_default",
"amname":"btree",
"include": ["name", "id"],
"columns":[{
"colname":"id",
"collspcname":"",
"op_class":"",
"sort_order":false,
"nulls":false,
"is_sort_nulls_applicable":true
}, {
"is_exp": true,
"colname":"lower(name)",
"collspcname":"pg_catalog.\"POSIX\"",
"op_class":"text_pattern_ops",
"sort_order":false,
"nulls":false,
"is_sort_nulls_applicable":true
}],
"description":"Test Comment",
"fillfactor":"10",
"indisunique":true,
"indisclustered":false,
"isconcurrent":false,
"indconstraint":"id < 100"
},
"expected_sql_file": "create_btree_expr_asc_null_last.sql",
"expected_msql_file": "create_btree_expr_asc_null_last_msql.sql"
},
{
"type": "alter",
"name": "Alter index statistics of expression -- 15 Plus",
"endpoint": "NODE-index.obj_id",
"sql_endpoint": "NODE-index.sql_id",
"msql_endpoint": "NODE-index.msql_id",
"data": {
"name": "Idx3_$%{}[]()&*^!@\"'`\\/#",
"columns":{
"changed": [{
"is_exp": true,
"col_num": 2,
"colname":"lower(name)",
"collspcname":"pg_catalog.\"POSIX\"",
"op_class":"text_pattern_ops",
"sort_order":false,
"nulls":false,
"is_sort_nulls_applicable":true,
"statistics": 1000
}]
}
},
"expected_sql_file": "alter_expr_statistics.sql",
"expected_msql_file": "alter_expr_statistics_msql.sql"
},
{
"type": "delete",
"name": "Drop index -- 15 Plus",
"endpoint": "NODE-index.delete_id",
"data": {
"name": "Idx3_$%{}[]()&*^!@\"'`\\/#"
}
},
{
"type": "create",
"name": "Create hash index -- 15 Plus",

View File

@ -0,0 +1,13 @@
-- Index: Idx3_$%{}[]()&*^!@"'`\/#
-- DROP INDEX IF EXISTS public."Idx3_$%{}[]()&*^!@""'`\/#";
CREATE UNIQUE INDEX IF NOT EXISTS "Idx3_$%{}[]()&*^!@""'`\/#"
ON public.test_table_for_indexes USING btree
(id ASC NULLS LAST, lower(name) COLLATE pg_catalog."POSIX" text_pattern_ops ASC NULLS LAST)
WITH (fillfactor=10)
TABLESPACE pg_default
WHERE id < 100;
COMMENT ON INDEX public."Idx3_$%{}[]()&*^!@""'`\/#"
IS 'Test Comment';

View File

@ -0,0 +1,2 @@
ALTER INDEX IF EXISTS public."Idx3_$%{}[]()&*^!@""'`\/#"
ALTER COLUMN 2 SET STATISTICS 1000;

View File

@ -0,0 +1,13 @@
-- Index: Idx3_$%{}[]()&*^!@"'`\/#
-- DROP INDEX IF EXISTS public."Idx3_$%{}[]()&*^!@""'`\/#";
CREATE UNIQUE INDEX IF NOT EXISTS "Idx3_$%{}[]()&*^!@""'`\/#"
ON public.test_table_for_indexes USING btree
(id ASC NULLS LAST, lower(name) COLLATE pg_catalog."POSIX" text_pattern_ops ASC NULLS LAST)
WITH (fillfactor=10)
TABLESPACE pg_default
WHERE id < 100;
COMMENT ON INDEX public."Idx3_$%{}[]()&*^!@""'`\/#"
IS 'Test Comment';

View File

@ -0,0 +1,9 @@
CREATE UNIQUE INDEX "Idx3_$%{}[]()&*^!@""'`\/#"
ON public.test_table_for_indexes USING btree
(id ASC NULLS LAST, (lower(name)) COLLATE pg_catalog."POSIX" text_pattern_ops ASC NULLS LAST)
WITH (fillfactor=10)
TABLESPACE pg_default
WHERE id < 100;
COMMENT ON INDEX public."Idx3_$%{}[]()&*^!@""'`\/#"
IS 'Test Comment';

View File

@ -150,6 +150,75 @@
"name": "Idx_$%{}[]()&*^!@\"'`\\/#"
}
},
{
"type": "create",
"name": "Create btree index with expression and ASC and NULLS LAST",
"endpoint": "NODE-index.obj",
"sql_endpoint": "NODE-index.sql_id",
"msql_endpoint": "NODE-index.msql",
"data": {
"name":"Idx3_$%{}[]()&*^!@\"'`\\/#",
"spcname":"pg_default",
"amname":"btree",
"columns":[{
"colname":"id",
"collspcname":"",
"op_class":"",
"sort_order":false,
"nulls":false,
"is_sort_nulls_applicable":true
}, {
"is_exp": true,
"colname":"lower(name)",
"collspcname":"pg_catalog.\"POSIX\"",
"op_class":"text_pattern_ops",
"sort_order":false,
"nulls":false,
"is_sort_nulls_applicable":true
}],
"description":"Test Comment",
"fillfactor":"10",
"indisunique":true,
"indisclustered":false,
"isconcurrent":false,
"indconstraint":"id < 100"
},
"expected_sql_file": "create_btree_expr_asc_null_last.sql",
"expected_msql_file": "create_btree_expr_asc_null_last_msql.sql"
},
{
"type": "alter",
"name": "Alter index statistics of expression",
"endpoint": "NODE-index.obj_id",
"sql_endpoint": "NODE-index.sql_id",
"msql_endpoint": "NODE-index.msql_id",
"data": {
"name": "Idx3_$%{}[]()&*^!@\"'`\\/#",
"columns":{
"changed": [{
"is_exp": true,
"col_num": 2,
"colname":"lower(name)",
"collspcname":"pg_catalog.\"POSIX\"",
"op_class":"text_pattern_ops",
"sort_order":false,
"nulls":false,
"is_sort_nulls_applicable":true,
"statistics": 1000
}]
}
},
"expected_sql_file": "alter_expr_statistics.sql",
"expected_msql_file": "alter_expr_statistics_msql.sql"
},
{
"type": "delete",
"name": "Drop index",
"endpoint": "NODE-index.delete_id",
"data": {
"name": "Idx3_$%{}[]()&*^!@\"'`\\/#"
}
},
{
"type": "create",
"name": "Create btree index with DESC and NULLS FIRST",

View File

@ -79,7 +79,7 @@
}
},
{
"name": "Create index: With valid data mumtiple.",
"name": "Create index: With valid data with multiple columns.",
"is_positive_test": true,
"inventory_data": {},
"test_data": {
@ -110,6 +110,39 @@
"test_result_data": {}
}
},
{
"name": "Create index: With valid data with multiple columns and expressions.",
"is_positive_test": true,
"inventory_data": {},
"test_data": {
"name": "test_index_add",
"spcname": "pg_default",
"amname": "btree",
"columns": [
{
"is_exp": true,
"colname": "lower(name)",
"sort_order": false,
"nulls": false
},
{
"colname": "id",
"sort_order": true,
"nulls": false
}
],
"include": [
"name"
]
},
"mocking_required": false,
"mock_data": {},
"expected_data": {
"status_code": 200,
"error_msg": null,
"test_result_data": {}
}
},
{
"name": "Create index: With invalid data - No column name.",
"is_positive_test": false,
@ -507,6 +540,24 @@
},
"is_list": false
},
{
"name": "Put index statistics : With existing index id.",
"is_positive_test": true,
"update_statistics": true,
"inventory_data": {
},
"test_data": {
"columns": "PLACE_HOLDER"
},
"mocking_required": false,
"mock_data": {},
"expected_data": {
"status_code": 200,
"error_msg": null,
"test_result_data": {}
},
"is_list": false
},
{
"name": "Put index : With existing index id while server is down.",
"is_positive_test": false,

View File

@ -57,6 +57,8 @@ class IndexesUpdateTestCase(BaseTestGenerator):
self.schema_name,
self.table_name,
self.column_name)
if hasattr(self, "update_statistics"):
self.column_name = "lower(%s)" % self.column_name
self.index_name = "test_index_delete_%s" % (str(uuid.uuid4())[1:8])
self.index_id = indexes_utils.create_index(self.server, self.db_name,
self.schema_name,
@ -70,6 +72,15 @@ class IndexesUpdateTestCase(BaseTestGenerator):
self.index_name)
if not index_response:
raise Exception("Could not find the index to update.")
if hasattr(self, "update_statistics"):
index_details = indexes_utils.api_get_index(self, self.index_id)
self.test_data['columns'] = {'changed': [
{"is_exp": True, "col_num":
index_details.json['columns'][0]['col_num'],
"colname": self.column_name,
"nulls": False, "sort_order": False,
"statistics": "1000"}]}
self.data = self.test_data
self.data['oid'] = self.index_id

View File

@ -117,6 +117,9 @@ def get_column_details(conn, idx, data, mode='properties', template_path=None):
row['attdef'].strip('"'),
'collspcname': row['collnspname'],
'op_class': row['opcname'],
'col_num': row['attnum'],
'is_exp': row['is_exp'],
'statistics': row['statistics']
}
# ASC/DESC and NULLS works only with btree indexes
@ -245,6 +248,12 @@ def get_sql(conn, **kwargs):
raise ObjectGone(_('Could not find the index in the table.'))
old_data = dict(res['rows'][0])
# Add column details for current index
old_data = get_column_details(conn, idx, old_data)
update_column_data, update_column = \
_get_column_details_to_update(old_data, data)
# Remove opening and closing bracket as we already have in jinja
# template.
if 'using' in old_data and old_data['using'] is not None and \
@ -263,7 +272,8 @@ def get_sql(conn, **kwargs):
sql = render_template(
"/".join([template_path, 'update.sql']),
data=data, o_data=old_data, conn=conn
data=data, o_data=old_data, conn=conn,
update_column_data=update_column_data, update_column=update_column
)
else:
sql = _get_create_sql(data, template_path, conn, mode, name,
@ -353,3 +363,25 @@ def get_storage_params(amname):
"heap": []
}
return storage_parameters[amname]
def _get_column_details_to_update(old_data, data):
"""
This function returns the columns/expressions which need to update
:param old_data:
:param data:
:return:
"""
update_column_data = []
update_column = False
if 'columns' in data and 'changed' in data['columns']:
for index, col1 in enumerate(old_data['columns']):
for col2 in data['columns']['changed']:
if col1['col_num'] == col2['col_num'] and col1['statistics'] \
!= col2['statistics']:
update_column_data.append(col2)
update_column = True
break
return update_column_data, update_column

View File

@ -9,6 +9,8 @@ SELECT
END::text[] AS options,
i.attnum,
pg_catalog.pg_get_indexdef(i.indexrelid, i.attnum, true) as attdef,
CASE WHEN pg_catalog.pg_get_indexdef(i.indexrelid, i.attnum, true) = a.attname THEN FALSE ELSE TRUE END AS is_exp,
a.attstattarget as statistics,
CASE WHEN (o.opcdefault = FALSE) THEN o.opcname ELSE null END AS opcname,
op.oprname AS oprname,
CASE WHEN length(nspc.nspname::text) > 0 AND length(coll.collname::text) > 0 THEN

View File

@ -3,7 +3,7 @@ CREATE{% if data.indisunique %} UNIQUE{% endif %} INDEX{% if add_not_exists_clau
ON {{conn|qtIdent(data.schema, data.table)}} {% if data.amname %}USING {{conn|qtIdent(data.amname)}}{% endif %}
{% if mode == 'create' %}
({% for c in data.columns %}{% if loop.index != 1 %}, {% endif %}{{conn|qtIdent(c.colname)}}{% if c.collspcname %} COLLATE {{c.collspcname}}{% endif %}{% if c.op_class %}
({% for c in data.columns %}{% if loop.index != 1 %}, {% endif %}{% if c.is_exp %}({{c.colname}}){% else %}{{conn|qtIdent(c.colname)}}{% endif %}{% if c.collspcname %} COLLATE {{c.collspcname}}{% endif %}{% if c.op_class %}
{{c.op_class}}{% endif %}{% if data.amname is defined %}{% if c.sort_order is defined and c.is_sort_nulls_applicable %}{% if c.sort_order %} DESC{% else %} ASC{% endif %}{% endif %}{% if c.nulls is defined and c.is_sort_nulls_applicable %} NULLS {% if c.nulls %}
FIRST{% else %}LAST{% endif %}{% endif %}{% endif %}{% endfor %})
{% if data.include|length > 0 %}

View File

@ -3,7 +3,7 @@ CREATE{% if data.indisunique %} UNIQUE{% endif %} INDEX{% if add_not_exists_clau
ON {{conn|qtIdent(data.schema, data.table)}} {% if data.amname %}USING {{conn|qtIdent(data.amname)}}{% endif %}
{% if mode == 'create' %}
({% for c in data.columns %}{% if loop.index != 1 %}, {% endif %}{{conn|qtIdent(c.colname)}}{% if c.collspcname %} COLLATE {{c.collspcname}}{% endif %}{% if c.op_class %}
({% for c in data.columns %}{% if loop.index != 1 %}, {% endif %}{% if c.is_exp %}({{c.colname}}){% else %}{{conn|qtIdent(c.colname)}}{% endif %}{% if c.collspcname %} COLLATE {{c.collspcname}}{% endif %}{% if c.op_class %}
{{c.op_class}}{% endif %}{% if data.amname is defined %}{% if c.sort_order is defined and c.is_sort_nulls_applicable %}{% if c.sort_order %} DESC{% else %} ASC{% endif %}{% endif %}{% if c.nulls is defined and c.is_sort_nulls_applicable %} NULLS {% if c.nulls %}
FIRST{% else %}LAST{% endif %}{% endif %}{% endif %}{% endfor %})
{% if data.include|length > 0 %}

View File

@ -9,6 +9,8 @@ SELECT
END::text[] AS options,
i.attnum,
pg_catalog.pg_get_indexdef(i.indexrelid, i.attnum, true) as attdef,
CASE WHEN pg_catalog.pg_get_indexdef(i.indexrelid, i.attnum, true) = a.attname THEN FALSE ELSE TRUE END AS is_exp,
a.attstattarget as statistics,
CASE WHEN (o.opcdefault = FALSE) THEN o.opcname ELSE null END AS opcname,
op.oprname AS oprname,
CASE WHEN length(nspc.nspname::text) > 0 AND length(coll.collname::text) > 0 THEN

View File

@ -3,7 +3,7 @@ CREATE{% if data.indisunique %} UNIQUE{% endif %} INDEX{% if add_not_exists_clau
ON {{conn|qtIdent(data.schema, data.table)}} {% if data.amname %}USING {{conn|qtIdent(data.amname)}}{% endif %}
{% if mode == 'create' %}
({% for c in data.columns %}{% if loop.index != 1 %}, {% endif %}{{conn|qtIdent(c.colname)}}{% if c.collspcname %} COLLATE {{c.collspcname}}{% endif %}{% if c.op_class %}
({% for c in data.columns %}{% if loop.index != 1 %}, {% endif %}{% if c.is_exp %}({{c.colname}}){% else %}{{conn|qtIdent(c.colname)}}{% endif %}{% if c.collspcname %} COLLATE {{c.collspcname}}{% endif %}{% if c.op_class %}
{{c.op_class}}{% endif %}{% if data.amname is defined %}{% if c.sort_order is defined and c.is_sort_nulls_applicable %}{% if c.sort_order %} DESC{% else %} ASC{% endif %}{% endif %}{% if c.nulls is defined and c.is_sort_nulls_applicable %} NULLS {% if c.nulls %}
FIRST{% else %}LAST{% endif %}{% endif %}{% endif %}{% endfor %})
{% else %}

View File

@ -85,3 +85,12 @@ ALTER TABLE IF EXISTS {{conn|qtIdent(data.schema, data.table)}}
{% if data.description is defined and o_data.description != data.description %}
COMMENT ON INDEX {{conn|qtIdent(data.schema, data.name)}}
IS {{data.description|qtLiteral(conn)}};{% endif %}
{## Alter column statistics##}
{% if update_column %}
{% for col in update_column_data %}
ALTER INDEX IF EXISTS {{conn|qtIdent(data.schema, data.name)}}
ALTER COLUMN {{col.col_num}} SET STATISTICS {{col.statistics}};
{% endfor %}
{% endif %}

View File

@ -62,7 +62,7 @@ export default class PrivilegeRoleSchema extends BaseUISchema {
disabled : function(state) {
return !(
obj.nodeInfo &&
obj.nodeInfo.server.user.name == state['grantor']
obj.nodeInfo.server?.user?.name == state['grantor']
);
},
},

View File

@ -133,8 +133,12 @@ const useStyles = makeStyles((theme)=>({
}
}));
function DataTableHeader({headerGroups}) {
function DataTableHeader({headerGroups, viewHelperProps, schema}) {
const classes = useStyles();
/* Using ref so that schema variable is not frozen in columns closure */
const schemaRef = useRef(schema);
const sortIcon = (isDesc) => {
return isDesc ? ' 🔽' : ' 🔼';
};
@ -142,21 +146,24 @@ function DataTableHeader({headerGroups}) {
<div className={classes.tableContentWidth}>
{headerGroups.map((headerGroup, hi) => (
<div key={hi} {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column, ci) => (
<div key={ci} {...column.getHeaderProps()}>
<div {...(column.sortable ? column.getSortByToggleProps() : {})} className={clsx(classes.tableCell, classes.tableCellHeader)}>
{column.render('Header')}
<span>
{column.isSorted ? sortIcon(column.isSortedDesc) : ''}
</span>
{headerGroup.headers.map((column, ci) => {
let {modeSupported} = column.field ? getFieldMetaData(column.field, schemaRef.current, {}, viewHelperProps) : {modeSupported: true};
return( modeSupported &&
<div key={ci} {...column.getHeaderProps()}>
<div {...(column.sortable ? column.getSortByToggleProps() : {})} className={clsx(classes.tableCell, classes.tableCellHeader)}>
{column.render('Header')}
<span>
{column.isSorted ? sortIcon(column.isSortedDesc) : ''}
</span>
</div>
{!column.disableResizing &&
<div
{...column.getResizerProps()}
className={classes.resizer}
/>}
</div>
{!column.disableResizing &&
<div
{...column.getResizerProps()}
className={classes.resizer}
/>}
</div>
))}
);
})}
</div>
))}
</div>
@ -165,9 +172,11 @@ function DataTableHeader({headerGroups}) {
DataTableHeader.propTypes = {
headerGroups: PropTypes.array.isRequired,
viewHelperProps: PropTypes.object.isRequired,
schema: CustomPropTypes.schemaUI.isRequired,
};
function DataTableRow({index, row, totalRows, isResizing, isHovered, schema, schemaRef, accessPath, moveRow, setHoverIndex}) {
function DataTableRow({index, row, totalRows, isResizing, isHovered, schema, schemaRef, accessPath, moveRow, setHoverIndex, viewHelperProps}) {
const classes = useStyles();
const [key, setKey] = useState(false);
const depListener = useContext(DepListenerContext);
@ -282,13 +291,16 @@ function DataTableRow({index, row, totalRows, isResizing, isHovered, schema, sch
>
{row.cells.map((cell, ci) => {
let classNames = [classes.tableCell];
let {modeSupported} = cell.column.field? getFieldMetaData(cell.column.field, schemaRef.current, {}, viewHelperProps) : {modeSupported: true};
if(typeof(cell.column.id) == 'string' && cell.column.id.startsWith('btn-')) {
classNames.push(classes.btnCell);
}
if(cell.column.id == 'btn-edit' && row.isExpanded) {
classNames.push(classes.expandedIconCell);
}
return (
return (modeSupported &&
<div ref={cell.column.id == 'btn-reorder' ? dragHandleRef : null} key={ci} {...cell.getCellProps()} className={clsx(classNames)}>
{cell.render('Cell', {
reRenderRow: ()=>{setKey((currKey)=>!currKey);}
@ -490,13 +502,13 @@ export default function DataGridView({
/* Make sure to take the latest field info from schema */
field = _.find(schemaRef.current.fields, (f)=>f.id==field.id) || field;
let {editable, disabled} = getFieldMetaData(field, schemaRef.current, row.original || {}, viewHelperProps);
let {editable, disabled, modeSupported} = getFieldMetaData(field, schemaRef.current, row.original || {}, viewHelperProps);
if(_.isUndefined(field.cell)) {
console.error('cell is required ', field);
}
return <MappedCellControl rowIndex={row.index} value={value}
return modeSupported && <MappedCellControl rowIndex={row.index} value={value}
row={row.original} {...field}
readonly={!editable}
disabled={disabled}
@ -631,14 +643,14 @@ export default function DataGridView({
/>}
<DndProvider backend={HTML5Backend}>
<div {...getTableProps(()=>({style: {minWidth: 'unset'}}))} className={classes.table}>
<DataTableHeader headerGroups={headerGroups} />
<DataTableHeader headerGroups={headerGroups} viewHelperProps={viewHelperProps} schema={schema} />
<div {...getTableBodyProps()} className={classes.tableContentWidth}>
{rows.map((row, i) => {
prepareRow(row);
return <React.Fragment key={row.index}>
<DataTableRow index={i} row={row} totalRows={rows.length} isResizing={isResizing}
schema={schemaRef.current} schemaRef={schemaRef} accessPath={accessPath.concat([row.index])}
moveRow={moveRow} isHovered={i == hoverIndex} setHoverIndex={setHoverIndex} />
moveRow={moveRow} isHovered={i == hoverIndex} setHoverIndex={setHoverIndex} viewHelperProps={viewHelperProps}/>
{props.canEdit && row.isExpanded &&
<FormView value={row.original} viewHelperProps={viewHelperProps} dataDispatch={dataDispatch}
schema={schemaRef.current} accessPath={accessPath.concat([row.index])} isNested={true} className={classes.expandedForm}

View File

@ -10,121 +10,57 @@
import '../helper/enzyme.helper';
import { createMount } from '@material-ui/core/test-utils';
import * as nodeAjax from '../../../pgadmin/browser/static/js/node_ajax';
import IndexSchema, { getColumnSchema } from '../../../pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/static/js/index.ui';
import BaseUISchema from '../../../pgadmin/static/js/SchemaView/base_schema.ui';
import IndexSchema from '../../../pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/static/js/index.ui';
import {genericBeforeEach, getCreateView, getEditView, getPropertiesView} from '../genericFunctions';
class SchemaInColl extends BaseUISchema {
constructor(indexSchemaObj) {
super();
this.indexSchemaObj = indexSchemaObj;
}
get baseFields() {
return [{
id: 'collection', label: '', type: 'collection',
schema: this.indexSchemaObj,
editable: false,
canAdd: true, canEdit: false, canDelete: true, hasRole: true,
columns : ['name', 'consrc'],
}];
}
}
function getFieldDepChange(schema, id) {
return _.find(schema.fields, (f)=>f.id==id)?.depChange;
}
describe('IndexSchema', ()=>{
let mount;
describe('column schema describe', () => {
let columnSchemaObj = getColumnSchema({}, {server: {user: {name: 'postgres'}}}, {});
it('column schema collection', ()=>{
spyOn(nodeAjax, 'getNodeAjaxOptions').and.returnValue([]);
spyOn(nodeAjax, 'getNodeListByName').and.returnValue([]);
mount(getCreateView(columnSchemaObj));
mount(getEditView(columnSchemaObj, getInitData));
});
it('column schema colname editable', ()=>{
columnSchemaObj._top = {
_sessData: { amname: 'btree' }
};
let cell = _.find(columnSchemaObj.fields, (f)=>f.id=='op_class').cell;
cell();
});
it('column schema sort_order depChange', ()=>{
let topState = { amname: 'btree' };
let depChange = _.find(columnSchemaObj.fields, (f)=>f.id=='sort_order').depChange;
let state = { sort_order: true };
depChange(state, {}, topState, { oldState: { sort_order: false } });
state.sort_order = false;
topState.amname = 'abc';
depChange(state, {}, topState, { oldState: { sort_order: false } });
expect(state.is_sort_nulls_applicable).toBe(false);
});
it('column schema sort_order editable', ()=>{
columnSchemaObj._top = {
_sessData: { amname: 'btree' }
};
let state = {};
spyOn(columnSchemaObj, 'inSchemaWithModelCheck').and.returnValue(true);
let editable = _.find(columnSchemaObj.fields, (f)=>f.id=='sort_order').editable;
let status = editable(state);
expect(status).toBe(false);
spyOn(columnSchemaObj, 'inSchemaWithModelCheck').and.returnValue(false);
status = editable(state);
expect(status).toBe(true);
columnSchemaObj._top._sessData.amname = 'abc';
status = editable(state);
expect(status).toBe(false);
});
it('column schema nulls editable', ()=>{
columnSchemaObj._top = {
_sessData: { amname: 'btree' }
};
let state = {};
spyOn(columnSchemaObj, 'inSchemaWithModelCheck').and.returnValue(true);
let editable = _.find(columnSchemaObj.fields, (f)=>f.id=='nulls').editable;
let status = editable(state);
expect(status).toBe(false);
spyOn(columnSchemaObj, 'inSchemaWithModelCheck').and.returnValue(false);
status = editable(state);
expect(status).toBe(true);
columnSchemaObj._top._sessData.amname = 'abc';
status = editable(state);
expect(status).toBe(false);
});
it('column schema setOpClassTypes', ()=>{
columnSchemaObj._top = {
_sessData: { amname: 'btree' }
};
let options = [];
columnSchemaObj.op_class_types = [];
let status = columnSchemaObj.setOpClassTypes(options);
expect(status).toEqual([]);
columnSchemaObj.op_class_types = [];
options.push({label: '', value: ''});
columnSchemaObj.setOpClassTypes(options);
expect(columnSchemaObj.op_class_types.length).toBe(1);
});
});
let indexSchemaObj = new IndexSchema(
()=>getColumnSchema({}, {server: {user: {name: 'postgres'}}}, {}),
{
tablespaceList: ()=>[],
amnameList : ()=>[{label:'abc', value:'abc'}],
columnList: ()=>[{label:'abc', value:'abc'}],
},
{
node_info: {'server': { 'version': 110000} }
},
{
amname: 'btree'
}
);
let indexSchemaObj;
let getInitData = ()=>Promise.resolve({});
/* Use createMount so that material ui components gets the required context */
/* https://material-ui.com/guides/testing/#api */
beforeAll(()=>{
mount = createMount();
spyOn(nodeAjax, 'getNodeAjaxOptions').and.returnValue(Promise.resolve([]));
spyOn(nodeAjax, 'getNodeListByName').and.returnValue(Promise.resolve([]));
indexSchemaObj = new IndexSchema(
{
tablespaceList: ()=>[],
amnameList : ()=>[{label:'abc', value:'abc'}],
columnList: ()=>[{label:'abc', value:'abc'}],
collationList: ()=>[{label:'abc', value:'abc'}],
opClassList: ()=>[{label:'abc', value:'abc'}]
},
{
node_info: {'server': { 'version': 110000} }
},
{
amname: 'btree'
}
);
});
afterAll(() => {
@ -147,12 +83,150 @@ describe('IndexSchema', ()=>{
mount(getPropertiesView(indexSchemaObj, getInitData));
});
it('create collection', ()=>{
let schemaCollObj = new SchemaInColl(indexSchemaObj);
let ctrl = mount(getCreateView(schemaCollObj));
/* Make sure you hit every corner */
ctrl.find('DataGridView').at(0).find('PgIconButton[data-test="add-row"]').find('button').simulate('click');
});
it('changeColumnOptions', ()=>{
spyOn(indexSchemaObj.indexHeaderSchema, 'changeColumnOptions').and.callThrough();
let columns = [{label: 'label', value: 'value'}];
indexSchemaObj.changeColumnOptions(columns);
expect(indexSchemaObj.indexHeaderSchema.changeColumnOptions).toHaveBeenCalledWith(columns);
});
describe('IndexColHeaderSchema', ()=>{
it('getNewData', ()=>{
indexSchemaObj.indexHeaderSchema.columnOptions = [
{label: 'id', value: 'id'},
{label: 'name', value: 'name'}
];
spyOn(indexSchemaObj.indexColumnSchema, 'getNewData');
indexSchemaObj.indexHeaderSchema.getNewData({
is_exp: false,
colname: 'id',
expression: null,
});
expect(indexSchemaObj.indexColumnSchema.getNewData).toHaveBeenCalledWith({
is_exp: false,
colname: 'id',
});
indexSchemaObj.indexHeaderSchema.getNewData({
is_exp: true,
colname: null,
expression: 'abc',
});
expect(indexSchemaObj.indexColumnSchema.getNewData).toHaveBeenCalledWith({
is_exp: true,
colname: 'abc',
});
});
});
describe('IndexColumnSchema', ()=>{
it('column schema colname editable', ()=>{
indexSchemaObj.indexColumnSchema._top = {
_sessData: { amname: 'btree' }
};
let cell = _.find(indexSchemaObj.indexColumnSchema.fields, (f)=>f.id=='op_class').cell;
cell();
});
it('column schema sort_order depChange', ()=>{
let topState = { amname: 'btree' };
let depChange = _.find(indexSchemaObj.indexColumnSchema.fields, (f)=>f.id=='sort_order').depChange;
let state = { sort_order: true };
depChange(state, {}, topState, { oldState: { sort_order: false } });
state.sort_order = false;
topState.amname = 'abc';
depChange(state, {}, topState, { oldState: { sort_order: false } });
expect(state.is_sort_nulls_applicable).toBe(false);
});
it('column schema sort_order editable', ()=>{
indexSchemaObj.indexColumnSchema._top = {
_sessData: { amname: 'btree' }
};
let state = {};
spyOn(indexSchemaObj.indexColumnSchema, 'inSchemaWithModelCheck').and.returnValue(true);
let editable = _.find(indexSchemaObj.indexColumnSchema.fields, (f)=>f.id=='sort_order').editable;
let status = editable(state);
expect(status).toBe(false);
spyOn(indexSchemaObj.indexColumnSchema, 'inSchemaWithModelCheck').and.returnValue(false);
status = editable(state);
expect(status).toBe(true);
indexSchemaObj.indexColumnSchema._top._sessData.amname = 'abc';
status = editable(state);
expect(status).toBe(false);
});
it('column schema nulls editable', ()=>{
indexSchemaObj.indexColumnSchema._top = {
_sessData: { amname: 'btree' }
};
let state = {};
spyOn(indexSchemaObj.indexColumnSchema, 'inSchemaWithModelCheck').and.returnValue(true);
let editable = _.find(indexSchemaObj.indexColumnSchema.fields, (f)=>f.id=='nulls').editable;
let status = editable(state);
expect(status).toBe(false);
spyOn(indexSchemaObj.indexColumnSchema, 'inSchemaWithModelCheck').and.returnValue(false);
status = editable(state);
expect(status).toBe(true);
indexSchemaObj.indexColumnSchema._top._sessData.amname = 'abc';
status = editable(state);
expect(status).toBe(false);
});
it('column schema setOpClassTypes', ()=>{
indexSchemaObj.indexColumnSchema._top = {
_sessData: { amname: 'btree' }
};
let options = [];
indexSchemaObj.indexColumnSchema.op_class_types = [];
let status = indexSchemaObj.indexColumnSchema.setOpClassTypes(options);
expect(status).toEqual([]);
indexSchemaObj.indexColumnSchema.op_class_types = [];
options.push({label: '', value: ''});
indexSchemaObj.indexColumnSchema.setOpClassTypes(options);
expect(indexSchemaObj.indexColumnSchema.op_class_types.length).toBe(1);
});
});
it('depChange', ()=>{
let state = {};
expect(getFieldDepChange(indexSchemaObj, 'description')(state)).toEqual({
comment: '',
});
});
it('columns formatter', ()=>{
let formatter = _.find(indexSchemaObj.fields, (f)=>f.id=='columns').cell().controlProps.formatter;
expect(formatter.fromRaw([{
colname: 'lid',
},{
colname: 'rid',
}])).toBe('lid, rid');
expect(formatter.fromRaw([])).toBe('');
});
it('validate', ()=>{
let state = { columns: [] };
let setError = jasmine.createSpy('setError');
indexSchemaObj.validate(state, setError);
expect(setError).toHaveBeenCalledWith('columns', 'You must specify at least one column.');
expect(setError).toHaveBeenCalledWith('columns', 'You must specify at least one column/expression.');
state.columns.push({});
let status = indexSchemaObj.validate(state, setError);