Hi, Please find attached patch to represent array data in datagrid with curly braces and also allow user to save array with values like <null>, <empty string>, ' *""*' and "*''*"
-- *Harshal Dhumal* *Sr. Software Engineer* EnterpriseDB India: http://www.enterprisedb.com The Enterprise PostgreSQL Company
diff --git a/web/pgadmin/feature_tests/test_data.json b/web/pgadmin/feature_tests/test_data.json index a2457c2..ac00b0b 100644 --- a/web/pgadmin/feature_tests/test_data.json +++ b/web/pgadmin/feature_tests/test_data.json @@ -7,12 +7,19 @@ "4": ["", "Hello World", "text"], "5": ["\"\"", "", "text", "Two double quotes"], "6": ["\\\"\\\"", "\"\"", "text", "double backslash followed by a double quote"], - "7": ["\\\\\"\\\\\"", "\\\"\\\"", "text", "double backslash followed by a double quote"], + "7": ["\\\\\"\\\\\"", "\\\\\"\\\\\"", "text", "double backslash followed by a double quote"], "8": ["", "[null]", "text"], "9": ["", "[51,52]", "json"], "10": ["[61,62]", "[61,62]", "json"], "11": ["", "true", "bool"], - "12": ["", "[null]", "bool"] + "12": ["", "[null]", "bool"], + "13": ["", "[null]", "text[]"], + "14": ["{}", "{}", "text[]"], + "15": ["{data,,'',\"\",\\'\\',\\\"\\\"}", "{data,[null],,,'',\"\"}", "text[]"], + "16": ["{}", "{}", "int[]"], + "17": ["{123,,456}", "{123,[null],456}", "int[]"], + "18": ["", "[null]", "boolean[]"], + "19": ["{false,,true}", "{false,[null],true}", "boolean[]"] } } -} \ No newline at end of file +} diff --git a/web/pgadmin/feature_tests/view_data_dml_queries.py b/web/pgadmin/feature_tests/view_data_dml_queries.py index 0b2da0c..153d796 100644 --- a/web/pgadmin/feature_tests/view_data_dml_queries.py +++ b/web/pgadmin/feature_tests/view_data_dml_queries.py @@ -67,6 +67,13 @@ CREATE TABLE public.defaults json_null json, boolean_defaults boolean DEFAULT true, boolean_null boolean, + text_arr text[], + text_arr_empty text[], + text_arr_null text[], + int_arr integer[], + int_arr_empty integer[], + boolean_arr boolean[], + boolean_arr_null boolean[], CONSTRAINT defaults_pkey PRIMARY KEY (id) ) """ @@ -169,17 +176,16 @@ CREATE TABLE public.defaults ActionChains(self.driver).move_to_element(cell_el).double_click( cell_el ).perform() - cell_type = data[2] value = data[0] - if cell_type == 'int': + if cell_type in ['int', 'int[]']: if value == 'clear': cell_el.find_element_by_css_selector('input').clear() else: ActionChains(self.driver).send_keys(value).perform() - elif cell_type in ['text', 'json']: + elif cell_type in ['text', 'json', 'text[]', 'boolean[]']: self.page.find_by_xpath( "//*[contains(@class, 'pg_textarea')]").click() ActionChains(self.driver).send_keys(value).perform() @@ -231,7 +237,6 @@ CREATE TABLE public.defaults self.page.find_by_xpath(row0_cell0_xpath).click() self.page.find_by_xpath("//*[@id='btn-copy-row']").click() self.page.find_by_xpath("//*[@id='btn-paste-row']").click() - # Update primary key of copied cell self._update_cell(row1_cell1_xpath, [2, "", "int"]) self.page.find_by_xpath( @@ -261,13 +266,12 @@ CREATE TABLE public.defaults self._verify_row_data(False) def _add_row(self): - for idx in range(1, len(config_data.keys())): + for idx in range(1, len(config_data.keys()) + 1): cell_xpath = CheckForViewDataTest._get_cell_xpath( 'r'+str(idx), 1 ) time.sleep(0.2) self._update_cell(cell_xpath, config_data[str(idx)]) - self.page.find_by_id("btn-save").click() # Save data # There should be some delay after save button is clicked, as it # takes some time to complete save ajax call otherwise discard unsaved @@ -290,12 +294,9 @@ CREATE TABLE public.defaults # List of row values in an array cells = [el.text for el in result_row.find_elements_by_tag_name('div')] - - for idx in range(1, len(config_data.keys())): + for idx in range(1, len(config_data.keys()) + 1): # after copy & paste row, the first cell of row 1 and # row 2(being primary keys) won't match # see if cell values matched to actual value - if idx != 1 and not is_new_row: - self.assertEquals(cells[idx], config_data[str(idx)][1]) - elif is_new_row: + if (idx != 1 and not is_new_row) or is_new_row: self.assertEquals(cells[idx], config_data[str(idx)][1]) diff --git a/web/pgadmin/static/js/slickgrid/editors.js b/web/pgadmin/static/js/slickgrid/editors.js index f3a28dd..80f7cce 100644 --- a/web/pgadmin/static/js/slickgrid/editors.js +++ b/web/pgadmin/static/js/slickgrid/editors.js @@ -48,6 +48,10 @@ return $buttons; } + function is_valid_array(val) { + val = $.trim(val) + return !(val != "" && (val.charAt(0) != '{' || val.charAt(val.length - 1) != '}')); + } /* * This function handles the [default] and [null] values for cells * if row is copied, otherwise returns the editor value. @@ -189,15 +193,41 @@ this.loadValue = function (item) { var col = args.column; - if (_.isUndefined(item[args.column.field]) && col.has_default_val) { - $input.val(defaultValue = ""); - } - else if (item[args.column.field] === "") { - $input.val(defaultValue = "''"); + if (_.isUndefined(item[args.column.field]) || _.isNull(item[args.column.field])) { + $input.val(defaultValue = ""); + return; } - else { - $input.val(defaultValue = item[args.column.field]); - $input.select(); + + if (!args.column.is_array) { + if (item[args.column.field] === "") { + $input.val(defaultValue = "''"); + } else if (item[args.column.field] === "''") { + $input.val(defaultValue = "\\'\\'"); + } else if (item[args.column.field] === '""') { + $input.val(defaultValue = '\\"\\"'); + } else { + $input.val(defaultValue = item[args.column.field]); + $input.select(); + } + } else { + var data = []; + for (var k in item[args.column.field]) { + if (_.isUndefined(item[args.column.field][k]) || _.isNull(item[args.column.field][k])) { + data.push(''); + } else if (item[args.column.field][k] === "") { + data.push("''"); + } else if (item[args.column.field][k] === "''") { + data.push("\\'\\'"); + } else if (item[args.column.field][k] === '""') { + data.push('\\"\\"'); + } else { + data.push(item[args.column.field][k]); + $input.select(); + } + } + defaultValue = data; + $input.val('{' + data.join() +'}'); + } }; @@ -207,18 +237,43 @@ if (value === "") { return null; } - // single/double quotes represent an empty string - // If found return '' - else if (value === "''" || value === '""') { - return ''; - } - else { - // If found string literals - \"\", \'\', \\'\\' and \\\\'\\\\' - // then remove slashes. - value = value.replace("\\'\\'", "''"); - value = value.replace('\\"\\"', '""'); - value = value = value.replace(/\\\\/g, '\\'); - return value; + + if (!args.column.is_array) { + if (value === "''" || value === '""') { + return ''; + } else if (value === "\\'\\'") { + return "''"; + } else if (value === '\\"\\"') { + return '""'; + } else { + return value; + } + } else { + + // Remove leading { and trailing }. + // Also remove leading and trailing whitespaces. + var value = $.trim(value.slice(1, -1)); + + if(value == '') { + return []; + } + + var data = []; + value = value.split(','); + for (var k in value) { + if (value[k] == "") { + data.push(null); //empty string from editor is null value. + } else if (value[k] === "''" || value[k] === '""') { + data.push(''); // double quote from editor is blank string; + } else if (value[k] === "\\'\\'") { + data.push("''"); + } else if (value[k] === '\\"\\"') { + data.push('""'); + } else { + data.push(value[k]); + } + } + return data; } }; @@ -233,7 +288,7 @@ return false; } else { return (!($input.val() == "" && _.isNull(defaultValue))) && - ($input.val() != defaultValue); + ($input.val() !== defaultValue); } }; @@ -245,6 +300,13 @@ } } + if (args.column.is_array && !is_valid_array($input.val())) { + return { + valid: false, + msg: "Array must start with '{' and end with '}'" + }; + } + return { valid: true, msg: null @@ -859,7 +921,13 @@ this.loadValue = function (item) { defaultValue = item[args.column.field]; - $input.val(defaultValue); + + if(args.column.is_array && !_.isNull(defaultValue) && !_.isUndefined(defaultValue)) { + $input.val('{' + defaultValue.join() +'}'); + } else { + $input.val(defaultValue); + } + $input[0].defaultValue = defaultValue; $input.select(); }; @@ -868,6 +936,24 @@ if ($input.val() === "") { return null; } + + if(args.column.is_array) { + // Remove leading { and trailing }. + // Also remove leading and trailing whitespaces. + var val = $.trim($input.val().slice(1, -1)); + + if(val == '') { + return []; + } + val = val.split(','); + for (var k in val) { + if (val[k] == "") { + val[k] = null; //empty string from editor is null value. + } + } + return val; + } + return $input.val(); }; @@ -887,20 +973,47 @@ }; this.validate = function () { - if (isNaN($input.val())) { + var value = $input.val(); + if (!args.column.is_array && isNaN(value)) { return { valid: false, - msg: "Please enter a valid integer" + msg: "Please enter a valid number" }; } - if (args.column.validator) { - var validationResults = args.column.validator($input.val()); + var validationResults = args.column.validator(value); if (!validationResults.valid) { return validationResults; } } + if (args.column.is_array) { + if (!is_valid_array(value)) { + return { + valid: false, + msg: "Array must start with '{' and end with '}'" + }; + } + + var val = $.trim(value.slice(1, -1)), + arr; + + if(val == '') { + arr = []; + } else { + var arr = val.split(','); + } + + for (var k in arr) { + if (isNaN(arr[k])) { + return { + valid: false, + msg: "Please enter a valid numbers" + }; + } + } + } + return { valid: true, msg: null diff --git a/web/pgadmin/static/js/slickgrid/formatters.js b/web/pgadmin/static/js/slickgrid/formatters.js index 1a0d60b..5de2dce 100644 --- a/web/pgadmin/static/js/slickgrid/formatters.js +++ b/web/pgadmin/static/js/slickgrid/formatters.js @@ -14,20 +14,45 @@ "Numbers": NumbersFormatter, "Checkmark": CheckmarkFormatter, "Text": TextFormatter, + "Binary": BinaryFormatter, + "JsonStringArray": JsonArrayFormatter, + "NumbersArray": NumbersArrayFormatter, + "TextArray": TextArrayFormatter, } } }); - function JsonFormatter(row, cell, value, columnDef, dataContext) { - // If column has default value, set placeholder + function NullAndDefaultFormatter(row, cell, value, columnDef, dataContext) { if (_.isUndefined(value) && columnDef.has_default_val) { - return "<span class='pull-left disabled_cell'>[default]</span>"; + return "<span class='pull-left disabled_cell'>[default]</span>"; } else if ( (_.isUndefined(value) && columnDef.not_null) || (_.isUndefined(value) || value === null) ) { return "<span class='pull-left disabled_cell'>[null]</span>"; + } + return null; + } + + function NullAndDefaultNumberFormatter(row, cell, value, columnDef, dataContext) { + if (_.isUndefined(value) && columnDef.has_default_val) { + return "<span class='pull-right disabled_cell'>[default]</span>"; + } + else if ( + (_.isUndefined(value) && columnDef.not_null) || + (_.isUndefined(value) || value === null) + ) { + return "<span class='pull-right disabled_cell'>[null]</span>"; + } + return null; + } + + function JsonFormatter(row, cell, value, columnDef, dataContext) { + // If column has default value, set placeholder + var data = NullAndDefaultFormatter(row, cell, value, columnDef, dataContext); + if (data) { + return data; } else { // Stringify only if it's json object if (typeof value === "object" && !Array.isArray(value)) { @@ -48,57 +73,113 @@ } } - function NumbersFormatter(row, cell, value, columnDef, dataContext) { + function JsonArrayFormatter(row, cell, value, columnDef, dataContext) { // If column has default value, set placeholder - if (_.isUndefined(value) && columnDef.has_default_val) { - return "<span class='pull-right disabled_cell'>[default]</span>"; - } - else if ( - (_.isUndefined(value) || value === null || value === "") || - (_.isUndefined(value) && columnDef.not_null) - ) { - return "<span class='pull-right disabled_cell'>[null]</span>"; + var data = NullAndDefaultFormatter(row, cell, value, columnDef, dataContext); + if (data) { + return data; + } else { + var data = []; + for (var k in value) { + // Stringify only if it's json object + var v = value[k]; + if (typeof v === "object" && !Array.isArray(v)) { + return data.push(_.escape(JSON.stringify(v))); + } else if (Array.isArray(v)) { + var temp = []; + $.each(v, function(i, val) { + if (typeof val === "object") { + temp.push(JSON.stringify(val)); + } else { + temp.push(val) + } + }); + return data.push(_.escape("[" + temp.join() + "]")); + } else { + return data.push(_.escape(v)); + } + } + return '{' + data.join() + '}'; } - else { + } + + function NumbersFormatter(row, cell, value, columnDef, dataContext) { + // If column has default value, set placeholder + var data = NullAndDefaultNumberFormatter(row, cell, value, columnDef, dataContext); + if (data) { + return data; + } else { return "<span style='float:right'>" + _.escape(value) + "</span>"; } } + function NumbersArrayFormatter(row, cell, value, columnDef, dataContext) { + // If column has default value, set placeholder + var data = NullAndDefaultNumberFormatter(row, cell, value, columnDef, dataContext); + if (data) { + return data; + } else { + data = []; + for(var k in value) { + if (value[k] == null) { + data.push("<span class='disabled_cell'>[null]</span>"); + } else { + data.push(_.escape(value[k])); + } + } + return "<span style='float:right'>{" + data.join() + "}</span>"; + } + } + function CheckmarkFormatter(row, cell, value, columnDef, dataContext) { /* Checkbox has 3 states * 1) checked=true * 2) unchecked=false * 3) indeterminate=null */ - if (_.isUndefined(value) && columnDef.has_default_val) { - return "<span class='pull-left disabled_cell'>[default]</span>"; - } - else if ( - (_.isUndefined(value) && columnDef.not_null) || - (value == null || value === "") - ) { - return "<span class='pull-left disabled_cell'>[null]</span>"; + var data = NullAndDefaultFormatter(row, cell, value, columnDef, dataContext); + if (data) { + return data; + } else { + return value ? "true" : "false"; } - return value ? "true" : "false"; } function TextFormatter(row, cell, value, columnDef, dataContext) { // If column has default value, set placeholder - if (_.isUndefined(value) && columnDef.has_default_val) { - return "<span class='pull-left disabled_cell'>[default]</span>"; - } - else if ( - (_.isUndefined(value) && columnDef.not_null) || - (_.isUndefined(value) || _.isNull(value)) - ) { - return "<span class='pull-left disabled_cell'>[null]</span>"; - } else if(columnDef.column_type_internal == 'bytea' || - columnDef.column_type_internal == 'bytea[]') { - return "<span class='pull-left disabled_cell'>[" + _.escape(value) + "]</span>"; - } - else { + var data = NullAndDefaultFormatter(row, cell, value, columnDef, dataContext); + if (data) { + return data; + } else { return _.escape(value); } } + function TextArrayFormatter(row, cell, value, columnDef, dataContext) { + // If column has default value, set placeholder + var data = NullAndDefaultFormatter(row, cell, value, columnDef, dataContext); + if (data) { + return data; + } else { + data = []; + for(var k in value) { + if (value[k] === null) { + data.push("<span class='disabled_cell'>[null]</span>"); + } else { + data.push(_.escape(value[k])); + } + } + return "{" + data.join() + "}"; + } + } + + function BinaryFormatter(row, cell, value, columnDef, dataContext) { + // If column has default value, set placeholder + var data = NullAndDefaultFormatter(row, cell, value, columnDef, dataContext); + if (data) { + return data; + } else { + return "<span class='pull-left disabled_cell'>[" + _.escape(value) + "]</span>"; + } + } })(jQuery); diff --git a/web/pgadmin/tools/sqleditor/command.py b/web/pgadmin/tools/sqleditor/command.py index 94aec09..3500d3c 100644 --- a/web/pgadmin/tools/sqleditor/command.py +++ b/web/pgadmin/tools/sqleditor/command.py @@ -446,7 +446,8 @@ class TableCommand(GridCommand): query_res = dict() count = 0 list_of_rowid = [] - list_of_sql = [] + operations = ('added', 'updated', 'deleted') + list_of_sql = {} _rowid = None if conn.connected(): @@ -457,7 +458,7 @@ class TableCommand(GridCommand): # Iterate total number of records to be updated/inserted for of_type in changed_data: # No need to go further if its not add/update/delete operation - if of_type not in ('added', 'updated', 'deleted'): + if of_type not in operations: continue # if no data to be save then continue if len(changed_data[of_type]) < 1: @@ -480,6 +481,7 @@ class TableCommand(GridCommand): # For newly added rows if of_type == 'added': + list_of_sql[of_type] = [] # When new rows are added, only changed columns data is # sent from client side. But if column is not_null and has @@ -506,12 +508,13 @@ class TableCommand(GridCommand): nsp_name=self.nsp_name, data_type=column_type, pk_names=pk_names) - list_of_sql.append(sql) + list_of_sql[of_type].append({'sql': sql, 'data': data}) # Reset column data column_data = {} # For updated rows elif of_type == 'updated': + list_of_sql[of_type] = [] for each_row in changed_data[of_type]: data = changed_data[of_type][each_row]['data'] pk = changed_data[of_type][each_row]['primary_keys'] @@ -521,11 +524,12 @@ class TableCommand(GridCommand): object_name=self.object_name, nsp_name=self.nsp_name, data_type=column_type) - list_of_sql.append(sql) - list_of_rowid.append(data) + list_of_sql[of_type].append({'sql': sql, 'data': data}) + list_of_rowid.append(data.get(client_primary_key)) # For deleted rows elif of_type == 'deleted': + list_of_sql[of_type] = [] is_first = True rows_to_delete = [] keys = None @@ -559,33 +563,35 @@ class TableCommand(GridCommand): no_of_keys=no_of_keys, object_name=self.object_name, nsp_name=self.nsp_name) - list_of_sql.append(sql) - - for i, sql in enumerate(list_of_sql): - if sql: - status, res = conn.execute_void(sql) - rows_affected = conn.rows_affected() - - # store the result of each query in dictionary - query_res[count] = {'status': status, 'result': res, - 'sql': sql, 'rows_affected': rows_affected} - count += 1 - - if not status: - conn.execute_void('ROLLBACK;') - # If we roll backed every thing then update the message for - # each sql query. - for val in query_res: - if query_res[val]['status']: - query_res[val]['result'] = 'Transaction ROLLBACK' - - # If list is empty set rowid to 1 - try: - _rowid = list_of_rowid[i] if list_of_rowid else 1 - except Exception: - _rowid = 0 - - return status, res, query_res, _rowid + list_of_sql[of_type].append({'sql': sql, 'data': {}}) + + for opr, sqls in list_of_sql.items(): + for item in sqls: + if item['sql']: + status, res = conn.execute_void( + item['sql'], item['data']) + rows_affected = conn.rows_affected() + + # store the result of each query in dictionary + query_res[count] = {'status': status, 'result': res, + 'sql': sql, 'rows_affected': rows_affected} + + if not status: + conn.execute_void('ROLLBACK;') + # If we roll backed every thing then update the message for + # each sql query. + for val in query_res: + if query_res[val]['status']: + query_res[val]['result'] = 'Transaction ROLLBACK' + + # If list is empty set rowid to 1 + try: + _rowid = list_of_rowid[count] if list_of_rowid else 1 + except Exception: + _rowid = 0 + + return status, res, query_res, _rowid + count += 1 # Commit the transaction if there is no error found conn.execute_void('COMMIT;') diff --git a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js index fa9d269..622663f 100644 --- a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js +++ b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js @@ -577,7 +577,8 @@ define('tools.querytool', [ column_type: c.column_type, column_type_internal: c.column_type_internal, not_null: c.not_null, - has_default_val: c.has_default_val + has_default_val: c.has_default_val, + is_array: c.is_array }; // Get the columns width based on longer string among data type or @@ -592,26 +593,28 @@ define('tools.querytool', [ else { options['width'] = column_size[table_name][c.name]; } - // If grid is editable then add editor else make it readonly if (c.cell == 'Json') { options['editor'] = is_editable ? Slick.Editors.JsonText : Slick.Editors.ReadOnlyJsonText; - options['formatter'] = Slick.Formatters.JsonString; + options['formatter'] = c.is_array ? Slick.Formatters.JsonStringArray : Slick.Formatters.JsonString; } else if (c.cell == 'number' || $.inArray(c.type, ['oid', 'xid', 'real']) !== -1 ) { options['editor'] = is_editable ? Slick.Editors.CustomNumber : Slick.Editors.ReadOnlyText; - options['formatter'] = Slick.Formatters.Numbers; + options['formatter'] = c.is_array ? Slick.Formatters.NumbersArray : Slick.Formatters.Numbers; } else if (c.cell == 'boolean') { options['editor'] = is_editable ? Slick.Editors.Checkbox : Slick.Editors.ReadOnlyCheckbox; - options['formatter'] = Slick.Formatters.Checkmark; - } else { + options['formatter'] = c.is_array ? Slick.Formatters.CheckmarkArray : Slick.Formatters.Checkmark; + } else if (c.cell == 'binary') { + // We do not support editing binary data in SQL editor and data grid. + options['formatter'] = Slick.Formatters.Binary; + }else { options['editor'] = is_editable ? Slick.Editors.pgText : Slick.Editors.ReadOnlypgText; - options['formatter'] = Slick.Formatters.Text; + options['formatter'] = c.is_array ? Slick.Formatters.TextArray : Slick.Formatters.Text; } grid_columns.push(options) @@ -2002,7 +2005,6 @@ define('tools.querytool', [ ',' + c.scale + ')' : ')'; } - // Identify cell type of column. switch (type) { case "json": @@ -2012,12 +2014,19 @@ define('tools.querytool', [ col_cell = 'Json'; break; case "smallint": + case "smallint[]": case "integer": + case "integer[]": case "bigint": + case "bigint[]": case "decimal": + case "decimal[]": case "numeric": + case "numeric[]": case "real": + case "real[]": case "double precision": + case "double precision[]": col_cell = 'number'; break; case "boolean": @@ -2033,25 +2042,31 @@ define('tools.querytool', [ } col_cell = 'string'; break; + case "bytea": + case "bytea[]": + col_cell = 'binary'; + break; default: col_cell = 'string'; } column_label = c.display_name + '<br>' + col_type; - var col = { - 'name': c.name, - 'display_name': c.display_name, - 'column_type': col_type, - 'column_type_internal': type, - 'pos': c.pos, - 'label': column_label, - 'cell': col_cell, - 'can_edit': self.can_edit, - 'type': type, - 'not_null': c.not_null, - 'has_default_val': c.has_default_val - }; + var array_type_bracket_index = type.lastIndexOf('[]'), + col = { + 'name': c.name, + 'display_name': c.display_name, + 'column_type': col_type, + 'column_type_internal': type, + 'pos': c.pos, + 'label': column_label, + 'cell': col_cell, + 'can_edit': self.can_edit, + 'type': type, + 'not_null': c.not_null, + 'has_default_val': c.has_default_val, + 'is_array': array_type_bracket_index > -1 && array_type_bracket_index + 2 == type.length + }; columns.push(col); }); @@ -2981,7 +2996,15 @@ define('tools.querytool', [ ), copied_rows = rows.map(function (rowIndex) { return data[rowIndex]; - }); + }), + array_types = []; + + // for quick look up create list of array data types + for (var k in self.columns) { + if (self.columns[k].is_array) { + array_types.push(self.columns[k].name); + } + } rows = rows.length == 0 ? self.last_copied_rows : rows @@ -3000,7 +3023,8 @@ define('tools.querytool', [ _.each(arr, function (val, i) { if (arr[i] !== undefined) { - if (_.isObject(arr[i])) { + // Do not stringify array types. + if (_.isObject(arr[i]) && array_types.indexOf(i) == -1) { obj[String(i)] = JSON.stringify(arr[i]); } else { obj[String(i)] = arr[i]; diff --git a/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/insert.sql b/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/insert.sql index f7139e4..23ffcb4 100644 --- a/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/insert.sql +++ b/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/insert.sql @@ -4,13 +4,5 @@ INSERT INTO {{ conn|qtIdent(nsp_name, object_name) }} ( {% if not loop.first %}, {% endif %}{{ conn|qtIdent(col) }}{% endfor %} ) VALUES ( {% for col in data_to_be_saved %} -{########################################################} -{# THIS IS TO CHECK IF DATA TYPE IS ARRAY? #} -{% if data_type[col].endswith('[]') %} -{% set col_value = "{%s}"|format(data_to_be_saved[col])|qtLiteral %} -{% else %} -{% set col_value = data_to_be_saved[col]|qtLiteral %} -{% endif %} -{########################################################} -{% if not loop.first %}, {% endif %}{{ col_value }}{% endfor %} -); \ No newline at end of file +{% if not loop.first %}, {% endif %}%({{ col }})s::{{ data_type[col] }}{% endfor %} +); diff --git a/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/update.sql b/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/update.sql index da649e3..c9dfddf 100644 --- a/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/update.sql +++ b/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/update.sql @@ -1,15 +1,7 @@ {# Update the row with primary keys (specified in primary_keys) #} UPDATE {{ conn|qtIdent(nsp_name, object_name) }} SET {% for col in data_to_be_saved %} -{########################################################} -{# THIS IS TO CHECK IF DATA TYPE IS ARRAY? #} -{% if data_type[col].endswith('[]') %} -{% set col_value = "{%s}"|format(data_to_be_saved[col])|qtLiteral %} -{% else %} -{% set col_value = data_to_be_saved[col]|qtLiteral %} -{% endif %} -{########################################################} -{% if not loop.first %}, {% endif %}{{ conn|qtIdent(col) }} = {{ col_value }}{% endfor %} +{% if not loop.first %}, {% endif %}{{ conn|qtIdent(col) }} = %({{ col }})s::{{ data_type[col] }}{% endfor %} WHERE {% for pk in primary_keys %} -{% if not loop.first %} AND {% endif %}{{ conn|qtIdent(pk) }} = {{ primary_keys[pk]|qtLiteral }}{% endfor %}; \ No newline at end of file +{% if not loop.first %} AND {% endif %}{{ conn|qtIdent(pk) }} = {{ primary_keys[pk]|qtLiteral }}{% endfor %}; diff --git a/web/pgadmin/utils/driver/psycopg2/__init__.py b/web/pgadmin/utils/driver/psycopg2/__init__.py index 7cda5ef..11952e1 100644 --- a/web/pgadmin/utils/driver/psycopg2/__init__.py +++ b/web/pgadmin/utils/driver/psycopg2/__init__.py @@ -50,32 +50,66 @@ else: _ = gettext +unicode_type_for_record = psycopg2.extensions.new_type( + (2249,), + "RECORD", + psycopg2.extensions.UNICODE +) + +unicode_array_type_for_record_array = psycopg2.extensions.new_array_type( + (2287,), + "ARRAY_RECORD", + unicode_type_for_record +) + # This registers a unicode type caster for datatype 'RECORD'. -psycopg2.extensions.register_type( - psycopg2.extensions.new_type((2249,), "RECORD", - psycopg2.extensions.UNICODE) +psycopg2.extensions.register_type(unicode_type_for_record) + +# This registers a array unicode type caster for datatype 'ARRAY_RECORD'. +psycopg2.extensions.register_type(unicode_array_type_for_record_array) + + +# define type caster to convert various pg types into string type +pg_types_to_string_type = psycopg2.extensions.new_type( + ( + # To cast bytea, interval type + 17, 1186, + + # to cast int4range, int8range, numrange tsrange, tstzrange, daterange + 3904, 3926, 3906, 3908, 3910, 3912, 3913, + + # date, timestamp, timestamptz, bigint, double precision + 1700, 1082, 1114, 1184, 20, 701, + + # real + 700 + ), + 'TYPECAST_TO_STRING', psycopg2.STRING ) -# This registers a type caster to convert various pg types into string type -psycopg2.extensions.register_type( - psycopg2.extensions.new_type( - ( - # To cast bytea, bytea[] and interval type - 17, 1001, 1186, - - # to cast int4range, int8range, numrange tsrange, tstzrange, - # daterange - 3904, 3926, 3906, 3908, 3910, 3912, 3913, - - # date, timestamp, timestamptz, bigint, double precision, bigint[] - 1700, 1082, 1114, 1184, 20, 701, 1016, - - # double precision[], real, real[] - 1022, 700, 1021 - ), - 'TYPECAST_TO_STRING', psycopg2.STRING) +# define type caster to convert pg array types of above types into +# array of string type +pg_array_types_to_array_of_string_type = psycopg2.extensions.new_array_type( + ( + # To cast bytea[] type + 1001, + + # bigint[] + 1016, + + # double precision[], real[] + 1022, 1021 + ), + 'TYPECAST_TO_ARRAY_OF_STRING', pg_types_to_string_type ) +# This registers a type caster to convert various pg types into string type +psycopg2.extensions.register_type(pg_types_to_string_type) + +# This registers a type caster to convert various pg array types into +# array of string type +psycopg2.extensions.register_type(pg_array_types_to_array_of_string_type) + def register_string_typecasters(connection): if connection.encoding != 'UTF8': @@ -145,6 +179,7 @@ def register_binary_typecasters(connection): connection ) + class Connection(BaseConnection): """ class Connection(object)