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 "[default]";
+ return "[default]";
}
else if (
(_.isUndefined(value) && columnDef.not_null) ||
(_.isUndefined(value) || value === null)
) {
return "[null]";
+ }
+ return null;
+ }
+
+ function NullAndDefaultNumberFormatter(row, cell, value, columnDef, dataContext) {
+ if (_.isUndefined(value) && columnDef.has_default_val) {
+ return "[default]";
+ }
+ else if (
+ (_.isUndefined(value) && columnDef.not_null) ||
+ (_.isUndefined(value) || value === null)
+ ) {
+ return "[null]";
+ }
+ 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 "[default]";
- }
- else if (
- (_.isUndefined(value) || value === null || value === "") ||
- (_.isUndefined(value) && columnDef.not_null)
- ) {
- return "[null]";
+ 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 "" + _.escape(value) + "";
}
}
+ 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("[null]");
+ } else {
+ data.push(_.escape(value[k]));
+ }
+ }
+ return "{" + data.join() + "}";
+ }
+ }
+
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 "[default]";
- }
- else if (
- (_.isUndefined(value) && columnDef.not_null) ||
- (value == null || value === "")
- ) {
- return "[null]";
+ 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 "[default]";
- }
- else if (
- (_.isUndefined(value) && columnDef.not_null) ||
- (_.isUndefined(value) || _.isNull(value))
- ) {
- return "[null]";
- } else if(columnDef.column_type_internal == 'bytea' ||
- columnDef.column_type_internal == 'bytea[]') {
- return "[" + _.escape(value) + "]";
- }
- 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("[null]");
+ } 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 "[" + _.escape(value) + "]";
+ }
+ }
})(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 + '
' + 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)