diff --git a/docs/en_US/images/query_data_editing.png b/docs/en_US/images/query_data_editing.png index c379e23c9..47ae2e20a 100644 Binary files a/docs/en_US/images/query_data_editing.png and b/docs/en_US/images/query_data_editing.png differ diff --git a/docs/en_US/query_tool_toolbar.rst b/docs/en_US/query_tool_toolbar.rst index 7c2469824..4d7b96e64 100644 --- a/docs/en_US/query_tool_toolbar.rst +++ b/docs/en_US/query_tool_toolbar.rst @@ -178,7 +178,12 @@ Data Editing Options | | | | | | * Click *Copy with headers* to copy the highlighted content along with the header. | | +----------------------+---------------------------------------------------------------------------------------------------+----------------+ - | *Paste* | Click the *Paste* icon to paste a previously copied row into a new row. | Accesskey + P | + | *Paste* | Click the *Paste* icon to paste a previously copied row with or without serial/identity values: | Accesskey + P | + | | | | + | | * Click the *Paste* icon to paste a previously copied row into a new row. | | + | | | | + | | * Click the *Paste with SERIAL/IDENTITY values?* if you want to paste the copied column values | | + | | in the serial/identity columns. | | +----------------------+---------------------------------------------------------------------------------------------------+----------------+ | *Delete* | Click the *Delete* icon to mark the selected rows for deletion. These marked rows get deleted | Accesskey + D | | | when you click the *Save Data Changes* icon. | | diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/columns/sql/default/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/columns/sql/default/nodes.sql index 660f12dd4..51bc13f8c 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/columns/sql/default/nodes.sql +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/columns/sql/default/nodes.sql @@ -1,5 +1,5 @@ SELECT DISTINCT att.attname as name, att.attnum as OID, pg_catalog.format_type(ty.oid,NULL) AS datatype, -att.attnotnull as not_null, att.atthasdef as has_default_val, des.description +att.attnotnull as not_null, att.atthasdef as has_default_val, des.description, seq.seqtypid FROM pg_catalog.pg_attribute att JOIN pg_catalog.pg_type ty ON ty.oid=atttypid JOIN pg_catalog.pg_namespace tn ON tn.oid=ty.typnamespace @@ -11,6 +11,7 @@ FROM pg_catalog.pg_attribute att LEFT OUTER JOIN pg_catalog.pg_namespace ns ON ns.oid=cs.relnamespace LEFT OUTER JOIN pg_catalog.pg_index pi ON pi.indrelid=att.attrelid AND indisprimary LEFT OUTER JOIN pg_catalog.pg_description des ON (des.objoid=att.attrelid AND des.objsubid=att.attnum AND des.classoid='pg_class'::regclass) + LEFT OUTER JOIN pg_catalog.pg_sequence seq ON cs.oid=seq.seqrelid WHERE att.attrelid = {{ tid|qtLiteral(conn) }}::oid {% if clid %} diff --git a/web/pgadmin/tools/sqleditor/static/js/components/sections/ResultSet.jsx b/web/pgadmin/tools/sqleditor/static/js/components/sections/ResultSet.jsx index 0c7f5d347..f70843739 100644 --- a/web/pgadmin/tools/sqleditor/static/js/components/sections/ResultSet.jsx +++ b/web/pgadmin/tools/sqleditor/static/js/components/sections/ResultSet.jsx @@ -530,6 +530,8 @@ export class ResultSetUtils { 'not_null': c.not_null, 'has_default_val': c.has_default_val, 'is_array': arrayBracketIdx > -1 && arrayBracketIdx + 2 == columnTypeInternal.length, + 'seqtypid': c.seqtypid, + 'isPK': isPK }; } @@ -559,14 +561,19 @@ export class ResultSetUtils { return columns; } - processClipboardVal(columnVal, col, rawCopiedVal) { - if(columnVal === '') { + processClipboardVal(columnVal, col, rawCopiedVal, pasteSerials) { + if(columnVal === '' ) { if(col.has_default_val) { + // if column has default value columnVal = undefined; } else if(rawCopiedVal === null) { columnVal = null; } + } else if (col.has_default_val && col.seqtypid && !pasteSerials) { + // if column has default value and is serial type + columnVal = undefined; } + if(col.cell === 'boolean') { if(columnVal == 'true') { columnVal = true; @@ -581,7 +588,7 @@ export class ResultSetUtils { return columnVal; } - processRows(result, columns, fromClipboard=false) { + processRows(result, columns, fromClipboard=false, pasteSerials=false) { let retVal = []; if(!_.isArray(result) || !_.size(result)) { return retVal; @@ -598,7 +605,7 @@ export class ResultSetUtils { let columnVal = rec[col.pos]; /* If the source is clipboard, then it needs some extra handling */ if(fromClipboard) { - columnVal = this.processClipboardVal(columnVal, col, copiedRowsObjects[recIdx]?.[col.key]); + columnVal = this.processClipboardVal(columnVal, col, copiedRowsObjects[recIdx]?.[col.key], pasteSerials); } rowObj[col.key] = columnVal; } @@ -1207,14 +1214,14 @@ export function ResultSet() { }, [selectedRows, queryData, dataChangeStore, rows]); useEffect(()=>{ - const triggerAddRows = (_rows, fromClipboard)=>{ + const triggerAddRows = (_rows, fromClipboard, pasteSerials)=>{ let insPosn = 0; if(selectedRows.size > 0) { let selectedRowsSorted = Array.from(selectedRows); selectedRowsSorted.sort(); insPosn = _.findIndex(rows, (r)=>rowKeyGetter(r)==selectedRowsSorted[selectedRowsSorted.length-1])+1; } - let newRows = rsu.current.processRows(_rows, columns, fromClipboard); + let newRows = rsu.current.processRows(_rows, columns, fromClipboard, pasteSerials); setRows((prev)=>[ ...prev.slice(0, insPosn), ...newRows, diff --git a/web/pgadmin/tools/sqleditor/static/js/components/sections/ResultSetToolbar.jsx b/web/pgadmin/tools/sqleditor/static/js/components/sections/ResultSetToolbar.jsx index 9e708fc6c..1bdb46d7e 100644 --- a/web/pgadmin/tools/sqleditor/static/js/components/sections/ResultSetToolbar.jsx +++ b/web/pgadmin/tools/sqleditor/static/js/components/sections/ResultSetToolbar.jsx @@ -53,6 +53,7 @@ export function ResultSetToolbar({containerRef, canEdit, totalRowCount}) { const [checkedMenuItems, setCheckedMenuItems] = React.useState({}); /* Menu button refs */ const copyMenuRef = React.useRef(null); + const pasetMenuRef = React.useRef(null); const queryToolPref = queryToolCtx.preferences.sqleditor; @@ -72,8 +73,8 @@ export function ResultSetToolbar({containerRef, canEdit, totalRowCount}) { field_separator: queryToolPref.results_grid_field_separator, }); let copiedRows = copyUtils.getCopiedRows(); - eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_ADD_ROWS, copiedRows, true); - }, [queryToolPref]); + eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_ADD_ROWS, copiedRows, true, checkedMenuItems['paste_with_serials']); + }, [queryToolPref, checkedMenuItems['paste_with_serials']]); const copyData = ()=>{ eventBus.fireEvent(QUERY_TOOL_EVENTS.COPY_DATA, checkedMenuItems['copy_with_headers']); }; @@ -163,6 +164,8 @@ export function ResultSetToolbar({containerRef, canEdit, totalRowCount}) { name="menu-copyheader" ref={copyMenuRef} onClick={openMenu} /> } accesskey={shortcut_key(queryToolPref.btn_paste_row)} disabled={!canEdit} onClick={pasteRows} /> + } splitButton + name="menu-pasteoptions" ref={pasetMenuRef} onClick={openMenu} /> } accesskey={shortcut_key(queryToolPref.btn_delete_row)} disabled={buttonsDisabled['delete-rows'] || !canEdit} onClick={deleteRows} /> @@ -188,6 +191,14 @@ export function ResultSetToolbar({containerRef, canEdit, totalRowCount}) { > {gettext('Copy with headers')} + + {gettext('Paste with SERIAL/IDENTITY values?')} + ); } diff --git a/web/pgadmin/tools/sqleditor/utils/get_column_types.py b/web/pgadmin/tools/sqleditor/utils/get_column_types.py index 3af6b72b5..49035cdb2 100644 --- a/web/pgadmin/tools/sqleditor/utils/get_column_types.py +++ b/web/pgadmin/tools/sqleditor/utils/get_column_types.py @@ -49,6 +49,9 @@ def get_columns_types(is_query_tool, columns_info, table_oid, conn, has_oids): col['has_default_val'] = \ rset['rows'][key]['has_default_val'] + col_type['seqtypid'] = col['seqtypid'] = \ + rset['rows'][key]['seqtypid'] + else: for row in rset['rows']: if row['oid'] == col['table_column']: @@ -56,10 +59,14 @@ def get_columns_types(is_query_tool, columns_info, table_oid, conn, has_oids): col_type['has_default_val'] = \ col['has_default_val'] = row['has_default_val'] + + col_type['seqtypid'] = col['seqtypid'] = \ + rset['rows'][key]['seqtypid'] break else: col_type['not_null'] = col['not_null'] = None col_type['has_default_val'] = col['has_default_val'] = None + col_type['seqtypid'] = col['seqtypid'] = None return column_types