prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  no loader for url scheme: postgresql
  **Small description**
  
  When attempting to start visidata with
  ```
  vd postgresql:///localdb
  ```
  it fails with
  ```
  Error: no loader for url scheme: postgresql
  ```
  
  **Expected result**
  
  I would have expected it to work the same way 
  ```
  vd postgres:///localdb
  ```
  works, as [both URL schemes are valid](https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING).
  
  P.S.: This is somewhere in between a bug and a feature request. As it's super small and about something existing, I called it a bug. I will raise a PR to fix this shortly.
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `visidata/loaders/postgres.py`
  Content:
  ```
  1 from visidata import *
  2 
  3 __all__ = ['openurl_postgres', 'openurl_rds', 'PgTable', 'PgTablesSheet']
  4 
  5 option('postgres_schema', 'public', 'The desired schema for the Postgres database')
  6 
  7 def codeToType(type_code, colname):
  8     import psycopg2
  9     try:
  10         tname = psycopg2._psycopg.string_types[type_code].name
  11         if 'INTEGER' in tname:
  12             return int
  13         if 'STRING' in tname:
  14             return str
  15     except KeyError:
  16         vd.status('unknown postgres type_code %s for %s' % (type_code, colname))
  17     return anytype
  18 
  19 
  20 def openurl_rds(url, filetype=None):
  21     import boto3
  22     import psycopg2
  23 
  24     rds = boto3.client('rds')
  25     url = urlparse(url.given)
  26 
  27     _, region, dbname = url.path.split('/')
  28     token = rds.generate_db_auth_token(url.hostname, url.port, url.username, region)
  29 
  30     conn = psycopg2.connect(
  31                 user=url.username,
  32                 dbname=dbname,
  33                 host=url.hostname,
  34                 port=url.port,
  35                 password=token)
  36 
  37     return PgTablesSheet(dbname+"_tables", sql=SQL(conn))
  38 
  39 
  40 def openurl_postgres(url, filetype=None):
  41     import psycopg2
  42 
  43     url = urlparse(url.given)
  44     dbname = url.path[1:]
  45     conn = psycopg2.connect(
  46                 user=url.username,
  47                 dbname=dbname,
  48                 host=url.hostname,
  49                 port=url.port,
  50                 password=url.password)
  51 
  52     return PgTablesSheet(dbname+"_tables", sql=SQL(conn))
  53 
  54 
  55 class SQL:
  56     def __init__(self, conn):
  57         self.conn = conn
  58 
  59     def cur(self, qstr):
  60         import string
  61         randomname = ''.join(random.choice(string.ascii_uppercase) for _ in range(6))
  62         cur = self.conn.cursor(randomname)
  63         cur.execute(qstr)
  64         return cur
  65 
  66     @asyncthread
  67     def query_async(self, qstr, callback=None):
  68         with self.cur(qstr) as cur:
  69             callback(cur)
  70             cur.close()
  71 
  72 
  73 def cursorToColumns(cur, sheet):
  74     sheet.columns = []
  75     for i, coldesc in enumerate(cur.description):
  76         sheet.addColumn(ColumnItem(coldesc.name, i, type=codeToType(coldesc.type_code, coldesc.name)))
  77 
  78 
  79 # rowdef: (table_name, ncols)
  80 class PgTablesSheet(Sheet):
  81     rowtype = 'tables'
  82 
  83     def reload(self):
  84         schema = options.postgres_schema
  85         qstr = f'''
  86             SELECT relname table_name, column_count.ncols, reltuples::bigint est_nrows
  87                 FROM pg_class, pg_namespace, (
  88                     SELECT table_name, COUNT(column_name) AS ncols FROM information_schema.COLUMNS WHERE table_schema = '{schema}' GROUP BY table_name
  89                     ) AS column_count
  90                 WHERE  pg_class.relnamespace = pg_namespace.oid AND pg_namespace.nspname = '{schema}' AND column_count.table_name = relname;
  91         '''
  92 
  93         with self.sql.cur(qstr) as cur:
  94             self.nrowsPerTable = {}
  95 
  96             self.rows = []
  97             # try to get first row to make cur.description available
  98             r = cur.fetchone()
  99             if r:
  100                 self.addRow(r)
  101             cursorToColumns(cur, self)
  102             self.setKeys(self.columns[0:1])  # table_name is the key
  103 
  104             for r in cur:
  105                 self.addRow(r)
  106 
  107     def openRow(self, row):
  108         return PgTable(self.name+"."+row[0], source=row[0], sql=self.sql)
  109 
  110 
  111 # rowdef: tuple of values as returned by fetchone()
  112 class PgTable(Sheet):
  113     @asyncthread
  114     def reload(self):
  115         if self.options.postgres_schema:
  116             source = f"{self.options.postgres_schema}.{self.source}"
  117         else:
  118             source = self.source
  119         with self.sql.cur(f"SELECT * FROM {source}") as cur:
  120             self.rows = []
  121             r = cur.fetchone()
  122             if r:
  123                 self.addRow(r)
  124             cursorToColumns(cur, self)
  125             for r in cur:
  126                 self.addRow(r)
  127
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/visidata/loaders/postgres.py b/visidata/loaders/postgres.py
  --- a/visidata/loaders/postgres.py
  +++ b/visidata/loaders/postgres.py
  @@ -1,6 +1,6 @@
   from visidata import *
   
  -__all__ = ['openurl_postgres', 'openurl_rds', 'PgTable', 'PgTablesSheet']
  +__all__ = ['openurl_postgres', 'openurl_postgresql', 'openurl_rds', 'PgTable', 'PgTablesSheet']
   
   option('postgres_schema', 'public', 'The desired schema for the Postgres database')
   
  @@ -52,6 +52,9 @@
       return PgTablesSheet(dbname+"_tables", sql=SQL(conn))
   
   
  +openurl_postgresql=openurl_postgres
  +
  +
   class SQL:
       def __init__(self, conn):
           self.conn = conn
  
verification_info: {"golden_diff": "diff --git a/visidata/loaders/postgres.py b/visidata/loaders/postgres.py\n--- a/visidata/loaders/postgres.py\n+++ b/visidata/loaders/postgres.py\n@@ -1,6 +1,6 @@\n from visidata import *\n \n-__all__ = ['openurl_postgres', 'openurl_rds', 'PgTable', 'PgTablesSheet']\n+__all__ = ['openurl_postgres', 'openurl_postgresql', 'openurl_rds', 'PgTable', 'PgTablesSheet']\n \n option('postgres_schema', 'public', 'The desired schema for the Postgres database')\n \n@@ -52,6 +52,9 @@\n     return PgTablesSheet(dbname+\"_tables\", sql=SQL(conn))\n \n \n+openurl_postgresql=openurl_postgres\n+\n+\n class SQL:\n     def __init__(self, conn):\n         self.conn = conn\n", "issue": "no loader for url scheme: postgresql\n**Small description**\r\n\r\nWhen attempting to start visidata with\r\n```\r\nvd postgresql:///localdb\r\n```\r\nit fails with\r\n```\r\nError: no loader for url scheme: postgresql\r\n```\r\n\r\n**Expected result**\r\n\r\nI would have expected it to work the same way \r\n```\r\nvd postgres:///localdb\r\n```\r\nworks, as [both URL schemes are valid](https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING).\r\n\r\nP.S.: This is somewhere in between a bug and a feature request. As it's super small and about something existing, I called it a bug. I will raise a PR to fix this shortly.\n", "before_files": [{"content": "from visidata import *\n\n__all__ = ['openurl_postgres', 'openurl_rds', 'PgTable', 'PgTablesSheet']\n\noption('postgres_schema', 'public', 'The desired schema for the Postgres database')\n\ndef codeToType(type_code, colname):\n    import psycopg2\n    try:\n        tname = psycopg2._psycopg.string_types[type_code].name\n        if 'INTEGER' in tname:\n            return int\n        if 'STRING' in tname:\n            return str\n    except KeyError:\n        vd.status('unknown postgres type_code %s for %s' % (type_code, colname))\n    return anytype\n\n\ndef openurl_rds(url, filetype=None):\n    import boto3\n    import psycopg2\n\n    rds = boto3.client('rds')\n    url = urlparse(url.given)\n\n    _, region, dbname = url.path.split('/')\n    token = rds.generate_db_auth_token(url.hostname, url.port, url.username, region)\n\n    conn = psycopg2.connect(\n                user=url.username,\n                dbname=dbname,\n                host=url.hostname,\n                port=url.port,\n                password=token)\n\n    return PgTablesSheet(dbname+\"_tables\", sql=SQL(conn))\n\n\ndef openurl_postgres(url, filetype=None):\n    import psycopg2\n\n    url = urlparse(url.given)\n    dbname = url.path[1:]\n    conn = psycopg2.connect(\n                user=url.username,\n                dbname=dbname,\n                host=url.hostname,\n                port=url.port,\n                password=url.password)\n\n    return PgTablesSheet(dbname+\"_tables\", sql=SQL(conn))\n\n\nclass SQL:\n    def __init__(self, conn):\n        self.conn = conn\n\n    def cur(self, qstr):\n        import string\n        randomname = ''.join(random.choice(string.ascii_uppercase) for _ in range(6))\n        cur = self.conn.cursor(randomname)\n        cur.execute(qstr)\n        return cur\n\n    @asyncthread\n    def query_async(self, qstr, callback=None):\n        with self.cur(qstr) as cur:\n            callback(cur)\n            cur.close()\n\n\ndef cursorToColumns(cur, sheet):\n    sheet.columns = []\n    for i, coldesc in enumerate(cur.description):\n        sheet.addColumn(ColumnItem(coldesc.name, i, type=codeToType(coldesc.type_code, coldesc.name)))\n\n\n# rowdef: (table_name, ncols)\nclass PgTablesSheet(Sheet):\n    rowtype = 'tables'\n\n    def reload(self):\n        schema = options.postgres_schema\n        qstr = f'''\n            SELECT relname table_name, column_count.ncols, reltuples::bigint est_nrows\n                FROM pg_class, pg_namespace, (\n                    SELECT table_name, COUNT(column_name) AS ncols FROM information_schema.COLUMNS WHERE table_schema = '{schema}' GROUP BY table_name\n                    ) AS column_count\n                WHERE  pg_class.relnamespace = pg_namespace.oid AND pg_namespace.nspname = '{schema}' AND column_count.table_name = relname;\n        '''\n\n        with self.sql.cur(qstr) as cur:\n            self.nrowsPerTable = {}\n\n            self.rows = []\n            # try to get first row to make cur.description available\n            r = cur.fetchone()\n            if r:\n                self.addRow(r)\n            cursorToColumns(cur, self)\n            self.setKeys(self.columns[0:1])  # table_name is the key\n\n            for r in cur:\n                self.addRow(r)\n\n    def openRow(self, row):\n        return PgTable(self.name+\".\"+row[0], source=row[0], sql=self.sql)\n\n\n# rowdef: tuple of values as returned by fetchone()\nclass PgTable(Sheet):\n    @asyncthread\n    def reload(self):\n        if self.options.postgres_schema:\n            source = f\"{self.options.postgres_schema}.{self.source}\"\n        else:\n            source = self.source\n        with self.sql.cur(f\"SELECT * FROM {source}\") as cur:\n            self.rows = []\n            r = cur.fetchone()\n            if r:\n                self.addRow(r)\n            cursorToColumns(cur, self)\n            for r in cur:\n                self.addRow(r)\n", "path": "visidata/loaders/postgres.py"}], "after_files": [{"content": "from visidata import *\n\n__all__ = ['openurl_postgres', 'openurl_postgresql', 'openurl_rds', 'PgTable', 'PgTablesSheet']\n\noption('postgres_schema', 'public', 'The desired schema for the Postgres database')\n\ndef codeToType(type_code, colname):\n    import psycopg2\n    try:\n        tname = psycopg2._psycopg.string_types[type_code].name\n        if 'INTEGER' in tname:\n            return int\n        if 'STRING' in tname:\n            return str\n    except KeyError:\n        vd.status('unknown postgres type_code %s for %s' % (type_code, colname))\n    return anytype\n\n\ndef openurl_rds(url, filetype=None):\n    import boto3\n    import psycopg2\n\n    rds = boto3.client('rds')\n    url = urlparse(url.given)\n\n    _, region, dbname = url.path.split('/')\n    token = rds.generate_db_auth_token(url.hostname, url.port, url.username, region)\n\n    conn = psycopg2.connect(\n                user=url.username,\n                dbname=dbname,\n                host=url.hostname,\n                port=url.port,\n                password=token)\n\n    return PgTablesSheet(dbname+\"_tables\", sql=SQL(conn))\n\n\ndef openurl_postgres(url, filetype=None):\n    import psycopg2\n\n    url = urlparse(url.given)\n    dbname = url.path[1:]\n    conn = psycopg2.connect(\n                user=url.username,\n                dbname=dbname,\n                host=url.hostname,\n                port=url.port,\n                password=url.password)\n\n    return PgTablesSheet(dbname+\"_tables\", sql=SQL(conn))\n\n\nopenurl_postgresql=openurl_postgres\n\n\nclass SQL:\n    def __init__(self, conn):\n        self.conn = conn\n\n    def cur(self, qstr):\n        import string\n        randomname = ''.join(random.choice(string.ascii_uppercase) for _ in range(6))\n        cur = self.conn.cursor(randomname)\n        cur.execute(qstr)\n        return cur\n\n    @asyncthread\n    def query_async(self, qstr, callback=None):\n        with self.cur(qstr) as cur:\n            callback(cur)\n            cur.close()\n\n\ndef cursorToColumns(cur, sheet):\n    sheet.columns = []\n    for i, coldesc in enumerate(cur.description):\n        sheet.addColumn(ColumnItem(coldesc.name, i, type=codeToType(coldesc.type_code, coldesc.name)))\n\n\n# rowdef: (table_name, ncols)\nclass PgTablesSheet(Sheet):\n    rowtype = 'tables'\n\n    def reload(self):\n        schema = options.postgres_schema\n        qstr = f'''\n            SELECT relname table_name, column_count.ncols, reltuples::bigint est_nrows\n                FROM pg_class, pg_namespace, (\n                    SELECT table_name, COUNT(column_name) AS ncols FROM information_schema.COLUMNS WHERE table_schema = '{schema}' GROUP BY table_name\n                    ) AS column_count\n                WHERE  pg_class.relnamespace = pg_namespace.oid AND pg_namespace.nspname = '{schema}' AND column_count.table_name = relname;\n        '''\n\n        with self.sql.cur(qstr) as cur:\n            self.nrowsPerTable = {}\n\n            self.rows = []\n            # try to get first row to make cur.description available\n            r = cur.fetchone()\n            if r:\n                self.addRow(r)\n            cursorToColumns(cur, self)\n            self.setKeys(self.columns[0:1])  # table_name is the key\n\n            for r in cur:\n                self.addRow(r)\n\n    def openRow(self, row):\n        return PgTable(self.name+\".\"+row[0], source=row[0], sql=self.sql)\n\n\n# rowdef: tuple of values as returned by fetchone()\nclass PgTable(Sheet):\n    @asyncthread\n    def reload(self):\n        if self.options.postgres_schema:\n            source = f\"{self.options.postgres_schema}.{self.source}\"\n        else:\n            source = self.source\n        with self.sql.cur(f\"SELECT * FROM {source}\") as cur:\n            self.rows = []\n            r = cur.fetchone()\n            if r:\n                self.addRow(r)\n            cursorToColumns(cur, self)\n            for r in cur:\n                self.addRow(r)\n", "path": "visidata/loaders/postgres.py"}]}