Re: [pgAdmin][RM4590] Schema Backup fails with names that requires quoting

Aditya Toshniwal <[email protected]>
Newsgroups gmane.comp.db.postgresql.pgadmin.devel
Message-ID <CAM9w-_mwnOMS2HDySELRsQqUYF9X4aFbOauwJqAzMHB2iTYLSw@mail.gmail.com>
Hi Hackers,

Attached is the updated patch. Tested on MacOS, Windows 7, Ubuntu 16 and
works fine.
Kindly review.

On Wed, Oct 9, 2019 at 1:48 PM Akshay Joshi <[email protected]>
wrote:

> Hi Aditya
>
> I have tried your patch on MAC. Backup job is completed successfully but
> got the following message:
> pg_dump: last built-in OID is 16383
> pg_dump: no matching schemas were found
>
> Please verify it once again and send the modified patch.
>
> On Tue, Oct 8, 2019 at 5:46 PM Aditya Toshniwal <
> [email protected]> wrote:
>
>> Hi Hackers,
>>
>> Turned out to be an issue only on Mac. Backup works fine for other OS.
>> Attached is the patch to fix the issue on Mac. Code is changed to add
>> extra pairs of quotes wherever required on MacOS.
>>
>> Kindly review.
>>
>> On Thu, Oct 3, 2019 at 3:53 PM Aditya Toshniwal <
>> [email protected]> wrote:
>>
>>> Hi Hackers,
>>>
>>> Attached is the patch to fix the schema backup with names that requires
>>> double quotes.
>>> Command line has special meaning for double quotes and so the command is
>>> run without the double quotes if applied on objects. Code is changed to add
>>> extra pairs of quotes to save our object quotes.
>>>
>>> Also fixed a test case which was failing if certain paths had spaces in
>>> between.
>>>
>>> Kindly review.
>>>
>>> --
>>> Thanks and Regards,
>>> Aditya Toshniwal
>>> Sr. Software Engineer | EnterpriseDB India | Pune
>>> "Don't Complain about Heat, Plant a TREE"
>>>
>>
>>
>> --
>> Thanks and Regards,
>> Aditya Toshniwal
>> Sr. Software Engineer | EnterpriseDB India | Pune
>> "Don't Complain about Heat, Plant a TREE"
>>
>
>
> --
> *Thanks & Regards*
> *Akshay Joshi*
>
> *Sr. Software Architect*
> *EnterpriseDB Software India Private Limited*
> *Mobile: +91 976-788-8246*
>


-- 
Thanks and Regards,
Aditya Toshniwal
Sr. Software Engineer | EnterpriseDB India | Pune
"Don't Complain about Heat, Plant a TREE"
RM4590_V3.patch (application/octet-stream, 6.2 KB)
diff --git a/web/pgadmin/misc/bgprocess/__init__.py b/web/pgadmin/misc/bgprocess/__init__.py
index 765ad54b..be17a830 100644
--- a/web/pgadmin/misc/bgprocess/__init__.py
+++ b/web/pgadmin/misc/bgprocess/__init__.py
@@ -120,3 +120,18 @@ def stop_process(pid):
         return success_return()
     except LookupError as lerr:
         return gone(errormsg=str(lerr))
+
+
+def escape_dquotes_process_arg(arg):
+    # Double quotes has special meaning for shell command line and they are
+    # run without the double quotes. Add extra quotes to save our double
+    # quotes from stripping.
+
+    # This cannot be at common place as this file executes
+    # separately from pgadmin
+    dq_id = "#DQ#"
+
+    if arg.startswith('"') and arg.endswith('"'):
+        return r'{0}{1}{0}'.format(dq_id, arg)
+    else:
+        return arg
diff --git a/web/pgadmin/misc/bgprocess/process_executor.py b/web/pgadmin/misc/bgprocess/process_executor.py
old mode 100644
new mode 100755
index 6ac73ee8..8f3c423d
--- a/web/pgadmin/misc/bgprocess/process_executor.py
+++ b/web/pgadmin/misc/bgprocess/process_executor.py
@@ -60,6 +60,22 @@ else:
             )
 
 
+def unescape_dquotes_process_arg(arg):
+    # Double quotes has special meaning for shell command line and they are
+    # run without the double quotes.
+    #
+    # Remove the saviour #DQ#
+
+    # This cannot be at common place as this file executes
+    # separately from pgadmin
+    dq_id = "#DQ#"
+
+    if arg.startswith(dq_id) and arg.endswith(dq_id):
+        return '{0}'.format(arg[len(dq_id):-len(dq_id)])
+    else:
+        return arg
+
+
 def _log_exception():
     type_, value_, traceback_ = info = sys.exc_info()
 
@@ -274,14 +290,14 @@ def update_status(**kw):
         raise ValueError("Please verify pid and db_file arguments.")
 
 
-def execute():
+def execute(argv):
     """
     This function will execute the background process
 
     Returns:
         None
     """
-    command = sys.argv[1:]
+    command = argv[1:]
     args = dict()
     _log('Initialize the process execution: {0}'.format(command))
 
@@ -363,7 +379,7 @@ def execute():
                 process_stderr.log(data[1])
 
     # If executable not found or invalid arguments passed
-    except OSError:
+    except OSError as e:
         info = _log_exception()
         args.update({'exit_code': 500})
         if process_stderr:
@@ -421,6 +437,10 @@ def convert_environment_variables(env):
 
 if __name__ == '__main__':
 
+    argv = [
+        unescape_dquotes_process_arg(arg) for arg in sys.argv
+    ]
+
     _sys_encoding = sys.getdefaultencoding()
     if not _sys_encoding or _sys_encoding == 'ascii':
         # Fall back to 'utf-8', if we couldn't determine the default encoding,
@@ -466,7 +486,7 @@ if __name__ == '__main__':
             # Let's do the job assigning to it.
             try:
                 _log('Executing the command now from the detached child...')
-                execute()
+                execute(argv)
             except Exception:
                 _log_exception()
         else:
@@ -500,7 +520,7 @@ if __name__ == '__main__':
             }
 
             cmd = [sys.executable]
-            cmd.extend(sys.argv)
+            cmd.extend(argv)
 
             _log('[PARENT] Command executings: {0}'.format(cmd))
 
@@ -549,7 +569,7 @@ if __name__ == '__main__':
                 w.close()
 
                 _log('[CHILD] Start executing the background process...')
-                execute()
+                execute(argv)
             except Exception:
                 _log_exception()
                 sys.exit(1)
diff --git a/web/pgadmin/setup/tests/test_export_import_servers.py b/web/pgadmin/setup/tests/test_export_import_servers.py
index 564384ad..b4d6d4e3 100644
--- a/web/pgadmin/setup/tests/test_export_import_servers.py
+++ b/web/pgadmin/setup/tests/test_export_import_servers.py
@@ -36,13 +36,13 @@ class ImportExportServersTestCase(BaseTestGenerator):
 
         # Load the servers
         os.system(
-            "python %s --load-servers %s 2> %s" %
+            "python \"%s\" --load-servers \"%s\" 2> %s" %
             (setup, os.path.join(path, "servers.json"), os.devnull)
         )
 
         # And dump them again
         tf = tempfile.NamedTemporaryFile(delete=False)
-        os.system("python %s --dump-servers %s 2> %s" %
+        os.system("python \"%s\" --dump-servers \"%s\" 2> %s" %
                   (setup, tf.name, os.devnull))
 
         # Compare the JSON files, ignoring servers that exist in our
diff --git a/web/pgadmin/tools/backup/__init__.py b/web/pgadmin/tools/backup/__init__.py
index ac7b4ff3..23b96548 100644
--- a/web/pgadmin/tools/backup/__init__.py
+++ b/web/pgadmin/tools/backup/__init__.py
@@ -24,6 +24,7 @@ from pgadmin.utils.ajax import make_json_response, bad_request
 
 from config import PG_DEFAULT_DRIVER
 from pgadmin.model import Server
+from pgadmin.misc.bgprocess import escape_dquotes_process_arg
 
 # set template path for sql scripts
 MODULE_NAME = 'backup'
@@ -419,14 +420,19 @@ def create_backup_objects_job(sid):
 
     if 'schemas' in data:
         for s in data['schemas']:
-            args.extend(['--schema', s])
+            args.extend(['--schema', r'{0}'.format(
+                driver.qtIdent(conn, s).replace('"', '\"'))])
 
     if 'tables' in data:
         for s, t in data['tables']:
             args.extend([
-                '--table', driver.qtIdent(conn, s, t)
+                '--table', r'{0}'.format(
+                    driver.qtIdent(conn, s, t).replace('"', '\"'))
             ])
 
+    escaped_args = [
+        escape_dquotes_process_arg(arg) for arg in args
+    ]
     try:
         if backup_obj_type == 'objects':
             args.append(data['database'])
@@ -439,7 +445,7 @@ def create_backup_objects_job(sid):
                     *args,
                     database=data['database']
                 ),
-                cmd=utility, args=args
+                cmd=utility, args=escaped_args
             )
         else:
             p = BatchProcess(
@@ -452,7 +458,7 @@ def create_backup_objects_job(sid):
                     ) else data['file'],
                     *args
                 ),
-                cmd=utility, args=args
+                cmd=utility, args=escaped_args
             )
 
         manager.export_password_env(p.id)
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.