Ensure JSON data isn't modified in-flight by psycopg2 when using View/Edit data. Fixes #3600

This commit is contained in:
Aditya Toshniwal 2019-02-25 10:34:36 +00:00 committed by Dave Page
parent a5d39003b6
commit 59446bb4b5
7 changed files with 53 additions and 34 deletions

View File

@ -25,6 +25,7 @@ Bug fixes
| `Bug #3418 <https://redmine.postgresql.org/issues/3418>`_ - Allow editing of values in columns with the oid datatype which are not an actual row OID.
| `Bug #3544 <https://redmine.postgresql.org/issues/3544>`_ - Make the Query Tool tab titles more concise and useful.
| `Bug #3583 <https://redmine.postgresql.org/issues/3583>`_ - Update CodeMirror to 5.43.0 to resolve issues with auto-indent.
| `Bug #3600 <https://redmine.postgresql.org/issues/3600>`_ - Ensure JSON data isn't modified in-flight by psycopg2 when using View/Edit data.
| `Bug #3673 <https://redmine.postgresql.org/issues/3673>`_ - Modify the Download as CSV option to use the same connection as the Query Tool its running in so temporary tables etc. can be used.
| `Bug #3873 <https://redmine.postgresql.org/issues/3873>`_ - Fix context sub-menu alignment on Safari.
| `Bug #3906 <https://redmine.postgresql.org/issues/3906>`_ - Fix alignment of Close and Maximize button of Grant Wizard.

View File

@ -2,7 +2,7 @@
{% set add_union = false %}
{% if 'session_stats' in chart_names %}
{% set add_union = true %}
SELECT 'session_stats' AS chart_name, row_to_json(t) AS chart_data
SELECT 'session_stats' AS chart_name, row_to_json(t)::jsonb AS chart_data
FROM (SELECT
(SELECT count(*) FROM pg_stat_activity{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Total') }}",
(SELECT count(*) FROM pg_stat_activity WHERE state = 'active'{% if did %} AND datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Active') }}",
@ -14,7 +14,7 @@ UNION ALL
{% endif %}
{% if 'tps_stats' in chart_names %}
{% set add_union = true %}
SELECT 'tps_stats' AS chart_name, row_to_json(t) AS chart_data
SELECT 'tps_stats' AS chart_name, row_to_json(t)::jsonb AS chart_data
FROM (SELECT
(SELECT sum(xact_commit) + sum(xact_rollback) FROM pg_stat_database{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Transactions') }}",
(SELECT sum(xact_commit) FROM pg_stat_database{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Commits') }}",
@ -26,7 +26,7 @@ UNION ALL
{% endif %}
{% if 'ti_stats' in chart_names %}
{% set add_union = true %}
SELECT 'ti_stats' AS chart_name, row_to_json(t) AS chart_data
SELECT 'ti_stats' AS chart_name, row_to_json(t)::jsonb AS chart_data
FROM (SELECT
(SELECT sum(tup_inserted) FROM pg_stat_database{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Inserts') }}",
(SELECT sum(tup_updated) FROM pg_stat_database{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Updates') }}",
@ -38,7 +38,7 @@ UNION ALL
{% endif %}
{% if 'to_stats' in chart_names %}
{% set add_union = true %}
SELECT 'to_stats' AS chart_name, row_to_json(t) AS chart_data
SELECT 'to_stats' AS chart_name, row_to_json(t)::jsonb AS chart_data
FROM (SELECT
(SELECT sum(tup_fetched) FROM pg_stat_database{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Fetched') }}",
(SELECT sum(tup_returned) FROM pg_stat_database{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Returned') }}"
@ -49,7 +49,7 @@ UNION ALL
{% endif %}
{% if 'bio_stats' in chart_names %}
{% set add_union = true %}
SELECT 'bio_stats' AS chart_name, row_to_json(t) AS chart_data
SELECT 'bio_stats' AS chart_name, row_to_json(t)::jsonb AS chart_data
FROM (SELECT
(SELECT sum(blks_read) FROM pg_stat_database{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Reads') }}",
(SELECT sum(blks_hit) FROM pg_stat_database{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Hits') }}"

View File

@ -33,12 +33,12 @@
// return wrapper element
function getWrapper() {
return $('<div class=\'pg_text_editor\' />');
return $('<div class=\'pg-text-editor\' />');
}
// return textarea element
function getTextArea() {
return $('<textarea class=\'pg_textarea text-12\' hidefocus rows=5\'>');
return $('<textarea class=\'pg-textarea text-12\' hidefocus rows=5\'>');
}
// Generate and return editor buttons
@ -107,7 +107,11 @@
grid.copied_rows[row][cell] = 1;
}
} else {
item[args.column.field] = state;
if(column_type === 'jsonb') {
item[args.column.field] = JSON.parse(state);
} else {
item[args.column.field] = state;
}
}
}
@ -364,6 +368,7 @@
this.loadValue = function(item) {
var data = defaultValue = item[args.column.field];
/* If jsonb or array */
if (data && typeof data === 'object' && !Array.isArray(data)) {
data = JSON.stringify(data, null, 4);
} else if (Array.isArray(data)) {
@ -377,6 +382,7 @@
});
data = '[' + temp.join() + ']';
}
/* if json take as is */
$input.val(data);
$input.trigger('select');
};
@ -388,8 +394,12 @@
return $input.val();
};
this.applyValue = function(item, state) {
setValue(args, item, state, 'text');
this.applyValue = function(item, state){
if(args.column.column_type_internal === 'jsonb') {
setValue(args, item, state, 'jsonb');
} else {
setValue(args, item, state, 'text');
}
};
this.isValueChanged = function() {
@ -401,13 +411,17 @@
};
this.validate = function() {
if (args.column.validator) {
var validationResults = args.column.validator($input.val());
if (!validationResults.valid) {
return validationResults;
if(args.column.column_type_internal === 'jsonb') {
try {
JSON.parse($input.val());
} catch(e) {
$input.addClass('pg-text-invalid');
return {
valid: false,
};
}
}
$input.removeClass('pg-text-invalid');
return {
valid: true,
msg: null,

View File

@ -266,12 +266,6 @@ input.editor-checkbox:focus {
}
/* Style for text editor */
.pg_textarea {
width:250px;
height:80px;
border:0;
outline:0;
}
.pg_buttons {
text-align:right;
}

View File

@ -233,17 +233,26 @@ li.CodeMirror-hint-active {
border-top: none;
}
.pg_text_editor {
.pg-text-editor {
z-index:10000;
position:absolute;
background: $color-bg-theme;
padding: 0.25rem;
border: $panel-border;
box-shadow: $dropdown-box-shadow;
}
.pg_text_editor textarea {
resize: both;
& .pg-textarea {
width:250px;
height:80px;
border:0;
outline:0;
resize: both;
}
& .pg-text-invalid {
background: $color-danger-lighter;
}
}
.sql-editor-message {

View File

@ -18,7 +18,6 @@ from flask import session
from flask_babelex import gettext
import psycopg2
from psycopg2.extensions import adapt
from psycopg2.extras import Json as psycopg2_json
import config
from pgadmin.model import Server, User
@ -226,14 +225,7 @@ class Driver(BaseDriver):
@staticmethod
def qtLiteral(value):
adapted = None
# adapt function cannot adapt dict data type
# Used http://initd.org/psycopg/docs/extras.html#json-adaptation
if type(value) == dict:
adapted = psycopg2_json(value)
else:
adapted = adapt(value)
adapted = adapt(value)
# Not all adapted objects have encoding
# e.g.

View File

@ -17,6 +17,7 @@ import sys
from psycopg2 import STRING as _STRING
import psycopg2
from psycopg2.extensions import encodings
from psycopg2.extras import Json as psycopg2_json
from .encoding import configureDriverEncodings
@ -166,6 +167,14 @@ def register_global_typecasters():
# array of string type
psycopg2.extensions.register_type(pg_array_types_to_array_of_string_type)
# Treat JSON data as text because converting it to dict alters the data
# which should not happen as per postgres docs
psycopg2.extras.register_default_json(loads=lambda x: x)
# pysycopg2 adapt does not support dict by default. Need to register
# Used http://initd.org/psycopg/docs/extras.html#json-adaptation
psycopg2.extensions.register_adapter(dict, psycopg2_json)
def register_string_typecasters(connection):
# raw_unicode_escape used for SQL ASCII will escape the