From 76d51a4cefabaf10368b11e56bf6fd08208ea2e8 Mon Sep 17 00:00:00 2001
From: ABW <andrzejl@syncad.com>
Date: Thu, 10 Sep 2020 21:21:35 +0200
Subject: [PATCH] [ABW]: SQL function get_account renamed find_account_id vote
 related views/types/functions extended by id since database_api requires it
 new SQL function to cover find_votes functionality neither voter nor post is
 mandatory in list_votes calls fixed internal _get_post_id in bridge_api
 find_votes_impl no longer requires full context, just what it actually uses:
 db find_votes_impl no longer checks passed arguments, only its official API
 version does list_votes_impl removed, only its official version is needed
 official API find_votes/list_votes now properly pack response in dictionary
 with 'votes' list

---
 hive/db/schema.py                    | 231 ++++++++++++++++-----------
 hive/server/bridge_api/cursor.py     |   2 +-
 hive/server/bridge_api/methods.py    |   8 +-
 hive/server/bridge_api/objects.py    |   2 +-
 hive/server/bridge_api/thread.py     |   4 +-
 hive/server/condenser_api/methods.py |  10 +-
 hive/server/condenser_api/objects.py |   4 +-
 hive/server/database_api/methods.py  |  55 ++-----
 tests/tests_api                      |   2 +-
 9 files changed, 169 insertions(+), 149 deletions(-)

diff --git a/hive/db/schema.py b/hive/db/schema.py
index 28f0b96eb..ad5e2432a 100644
--- a/hive/db/schema.py
+++ b/hive/db/schema.py
@@ -466,6 +466,60 @@ def setup(db):
     sql = "CREATE INDEX hive_communities_ft1 ON hive_communities USING GIN (to_tsvector('english', title || ' ' || about))"
     db.query(sql)
 
+    sql = """
+      DROP FUNCTION IF EXISTS find_comment_id(character varying, character varying, boolean)
+      ;
+      CREATE OR REPLACE FUNCTION find_comment_id(
+        in _author hive_accounts.name%TYPE,
+        in _permlink hive_permlink_data.permlink%TYPE,
+        in _check boolean)
+      RETURNS INT
+      LANGUAGE 'plpgsql'
+      AS
+      $function$
+      DECLARE 
+        post_id INT;
+      BEGIN
+        SELECT INTO post_id COALESCE( (SELECT hp.id
+        FROM hive_posts hp
+        JOIN hive_accounts ha ON ha.id = hp.author_id
+        JOIN hive_permlink_data hpd ON hpd.id = hp.permlink_id
+        WHERE ha.name = _author AND hpd.permlink = _permlink AND hp.counter_deleted = 0
+        ), 0 );
+        IF _check AND (_author <> '' OR _permlink <> '') AND post_id = 0 THEN
+          RAISE EXCEPTION 'Post %/% does not exist', _author, _permlink;
+        END IF;
+        RETURN post_id;
+      END
+      $function$
+      ;
+    """
+    db.query_no_return(sql)
+
+    sql = """
+        DROP FUNCTION IF EXISTS find_account_id(character varying, boolean)
+        ;
+        CREATE OR REPLACE FUNCTION find_account_id(
+          in _account hive_accounts.name%TYPE,
+          in _check boolean)
+        RETURNS INT
+        LANGUAGE 'plpgsql'
+        AS
+        $function$
+        DECLARE 
+          account_id INT;
+        BEGIN
+          SELECT INTO account_id COALESCE( ( SELECT id FROM hive_accounts WHERE name=_account ), 0 );
+          IF _check AND account_id = 0 THEN
+            RAISE EXCEPTION 'Account % does not exist', _account;
+          END IF;
+          RETURN account_id;
+        END
+        $function$
+        ;
+    """
+    db.query_no_return(sql)
+
     sql = """
           DROP FUNCTION if exists process_hive_post_operation(character varying,character varying,character varying,character varying,timestamp without time zone,timestamp without time zone)
           ;
@@ -859,6 +913,7 @@ def setup(db):
         CREATE OR REPLACE VIEW hive_votes_view
         AS
         SELECT
+            hv.id,
             hv.voter_id as voter_id,
             ha_a.name as author,
             hpd.permlink as permlink,
@@ -885,6 +940,7 @@ def setup(db):
         DROP TYPE IF EXISTS database_api_vote CASCADE;
 
         CREATE TYPE database_api_vote AS (
+          id BIGINT,
           voter VARCHAR(16),
           author VARCHAR(16),
           permlink VARCHAR(255),
@@ -896,30 +952,47 @@ def setup(db):
           reputation FLOAT4
         );
 
-        DROP FUNCTION IF EXISTS get_account(character varying, boolean);
-
-        CREATE OR REPLACE FUNCTION get_account(
-          in _account hive_accounts.name%TYPE,
-          in _check boolean)
-        RETURNS INT
+        DROP FUNCTION IF EXISTS find_votes( character varying, character varying )
+        ;
+        CREATE OR REPLACE FUNCTION public.find_votes
+        (
+          in _AUTHOR hive_accounts.name%TYPE,
+          in _PERMLINK hive_permlink_data.permlink%TYPE
+        )
+        RETURNS SETOF database_api_vote
         LANGUAGE 'plpgsql'
         AS
         $function$
-        DECLARE 
-          account_id INT;
+        DECLARE _POST_ID INT;
         BEGIN
-          SELECT INTO account_id COALESCE( ( SELECT id FROM hive_accounts WHERE name=_account ), 0 );
-          IF _check AND account_id = 0 THEN
-            RAISE EXCEPTION 'Account % does not exist', _account;
-          END IF;
+        _POST_ID = find_comment_id( _AUTHOR, _PERMLINK, True);
 
-          RETURN account_id;
-        END
-        $function$
-        ;
+        RETURN QUERY
+        (
+            SELECT
+                v.id,
+                v.voter,
+                v.author,
+                v.permlink,
+                v.weight,
+                v.rshares,
+                v.percent,
+                v.last_update,
+                v.num_changes,
+                v.reputation
+            FROM
+                hive_votes_view v
+            WHERE
+                v.post_id = _POST_ID
+            ORDER BY
+                voter_id
+        );
 
-        DROP FUNCTION IF EXISTS list_votes_by_voter_comment( character varying, character varying, character varying, int );
+        END
+        $function$;
 
+        DROP FUNCTION IF EXISTS list_votes_by_voter_comment( character varying, character varying, character varying, int )
+        ;
         CREATE OR REPLACE FUNCTION public.list_votes_by_voter_comment
         (
           in _VOTER hive_accounts.name%TYPE,
@@ -935,38 +1008,39 @@ def setup(db):
         DECLARE _POST_ID INT;
         BEGIN
 
-        _VOTER_ID = get_account( _VOTER, true );
-        _POST_ID = find_comment_id( _AUTHOR, _PERMLINK, True);
+        _VOTER_ID = find_account_id( _VOTER, _VOTER != '' );
+        _POST_ID = find_comment_id( _AUTHOR, _PERMLINK, _AUTHOR != '' OR _PERMLINK != '' );
 
         RETURN QUERY
         (
-                SELECT
-                    v.voter,
-                    v.author,
-                    v.permlink,
-                    v.weight,
-                    v.rshares,
-                    v.percent,
-                    v.last_update,
-                    v.num_changes,
-                    v.reputation
-                FROM
-                    hive_votes_view v
-                    WHERE
-                        ( v.voter_id = _VOTER_ID and v.post_id >= _POST_ID )
-                        OR
-                        ( v.voter_id > _VOTER_ID )
-                    ORDER BY
-                      voter_id,
-                      post_id
-                LIMIT _LIMIT
+            SELECT
+                v.id,
+                v.voter,
+                v.author,
+                v.permlink,
+                v.weight,
+                v.rshares,
+                v.percent,
+                v.last_update,
+                v.num_changes,
+                v.reputation
+            FROM
+                hive_votes_view v
+            WHERE
+                ( v.voter_id = _VOTER_ID and v.post_id >= _POST_ID )
+                OR
+                ( v.voter_id > _VOTER_ID )
+            ORDER BY
+                voter_id,
+                post_id
+            LIMIT _LIMIT
         );
 
         END
         $function$;
 
-        DROP FUNCTION IF EXISTS list_votes_by_comment_voter( character varying, character varying, character varying, int );
-
+        DROP FUNCTION IF EXISTS list_votes_by_comment_voter( character varying, character varying, character varying, int )
+        ;
         CREATE OR REPLACE FUNCTION public.list_votes_by_comment_voter
         (
           in _VOTER hive_accounts.name%TYPE,
@@ -982,31 +1056,32 @@ def setup(db):
         DECLARE _POST_ID INT;
         BEGIN
 
-        _VOTER_ID = get_account( _VOTER, true );
-        _POST_ID = find_comment_id( _AUTHOR, _PERMLINK, True);
+        _VOTER_ID = find_account_id( _VOTER, _VOTER != '' );
+        _POST_ID = find_comment_id( _AUTHOR, _PERMLINK, _AUTHOR != '' OR _PERMLINK != '' );
 
         RETURN QUERY
         (
-                SELECT
-                    v.voter,
-                    v.author,
-                    v.permlink,
-                    v.weight,
-                    v.rshares,
-                    v.percent,
-                    v.last_update,
-                    v.num_changes,
-                    v.reputation
-                FROM
-                    hive_votes_view v
-                    WHERE
-                        ( v.post_id = _POST_ID and v.voter_id >= _VOTER_ID )
-                        OR
-                        ( v.post_id > _POST_ID )
-                    ORDER BY
-                      post_id,
-                      voter_id
-                LIMIT _LIMIT
+            SELECT
+                v.id,
+                v.voter,
+                v.author,
+                v.permlink,
+                v.weight,
+                v.rshares,
+                v.percent,
+                v.last_update,
+                v.num_changes,
+                v.reputation
+            FROM
+                hive_votes_view v
+            WHERE
+                ( v.post_id = _POST_ID and v.voter_id >= _VOTER_ID )
+                OR
+                ( v.post_id > _POST_ID )
+            ORDER BY
+                post_id,
+                voter_id
+            LIMIT _LIMIT
         );
 
         END
@@ -1014,36 +1089,6 @@ def setup(db):
     """
     db.query_no_return(sql)
 
-    sql = """
-      DROP FUNCTION IF EXISTS find_comment_id(character varying, character varying, boolean)
-      ;
-      CREATE OR REPLACE FUNCTION find_comment_id(
-        in _author hive_accounts.name%TYPE,
-        in _permlink hive_permlink_data.permlink%TYPE,
-        in _check boolean)
-      RETURNS INT
-      LANGUAGE 'plpgsql'
-      AS
-      $function$
-      DECLARE 
-        post_id INT;
-      BEGIN
-        SELECT INTO post_id COALESCE( (SELECT hp.id
-        FROM hive_posts hp
-        JOIN hive_accounts ha ON ha.id = hp.author_id
-        JOIN hive_permlink_data hpd ON hpd.id = hp.permlink_id
-        WHERE ha.name = _author AND hpd.permlink = _permlink AND hp.counter_deleted = 0
-        ), 0 );
-        IF _check AND (_author <> '' OR _permlink <> '') AND post_id = 0 THEN
-          RAISE EXCEPTION 'Post %/% does not exist', _author, _permlink;
-        END IF;
-        RETURN post_id;
-      END
-      $function$
-      ;
-    """
-    db.query_no_return(sql)
-
     sql = """
       DROP TYPE IF EXISTS database_api_post CASCADE;
       CREATE TYPE database_api_post AS (
diff --git a/hive/server/bridge_api/cursor.py b/hive/server/bridge_api/cursor.py
index c24c27e30..dc046a863 100644
--- a/hive/server/bridge_api/cursor.py
+++ b/hive/server/bridge_api/cursor.py
@@ -21,7 +21,7 @@ async def _get_post_id(db, author, permlink):
         INNER JOIN hive_accounts ha_a ON ha_a.id = hp.author_id
         INNER JOIN hive_permlink_data hpd_p ON hpd_p.id = hp.permlink_id
         WHERE ha_a.name = :author AND hpd_p.permlink = :permlink"""
-    post_id = await db.query_one(sql, a=author, p=permlink)
+    post_id = await db.query_one(sql, author=author, permlink=permlink)
     assert post_id, 'invalid author/permlink'
     return post_id
 
diff --git a/hive/server/bridge_api/methods.py b/hive/server/bridge_api/methods.py
index 970629487..40c7e5899 100644
--- a/hive/server/bridge_api/methods.py
+++ b/hive/server/bridge_api/methods.py
@@ -109,7 +109,7 @@ async def get_post(context, author, permlink, observer=None):
     result = await db.query_all(sql, author=author, permlink=permlink)
     assert len(result) == 1, 'invalid author/permlink or post not found in cache'
     post = _bridge_post_object(result[0])
-    post['active_votes'] = await find_votes_impl({'db':db}, author, permlink, VotesPresentation.BridgeApi)
+    post['active_votes'] = await find_votes_impl(db, author, permlink, VotesPresentation.BridgeApi)
     post = await append_statistics_to_post(post, result[0], False, blacklists_for_user)
     return post
 
@@ -213,7 +213,7 @@ async def get_ranked_posts(context, sort, start_author='', start_permlink='',
         pinned_result = await db.query_all(pinned_sql, author=start_author, limit=limit, tag=tag, permlink=start_permlink, community_name=tag, observer=observer)
         for row in pinned_result:
             post = _bridge_post_object(row)
-            post['active_votes'] = await find_votes_impl({'db':db}, row['author'], row['permlink'], VotesPresentation.BridgeApi)
+            post['active_votes'] = await find_votes_impl(db, row['author'], row['permlink'], VotesPresentation.BridgeApi)
             post = await append_statistics_to_post(post, row, True, blacklists_for_user)
             limit = limit - 1
             posts.append(post)
@@ -222,7 +222,7 @@ async def get_ranked_posts(context, sort, start_author='', start_permlink='',
     sql_result = await db.query_all(sql, author=start_author, limit=limit, tag=tag, permlink=start_permlink, community_name=tag, observer=observer)
     for row in sql_result:
         post = _bridge_post_object(row)
-        post['active_votes'] = await find_votes_impl({'db':db}, row['author'], row['permlink'], VotesPresentation.BridgeApi)
+        post['active_votes'] = await find_votes_impl(db, row['author'], row['permlink'], VotesPresentation.BridgeApi)
         post = await append_statistics_to_post(post, row, False, blacklists_for_user)
         if post['post_id'] in pinned_post_ids:
             continue
@@ -314,7 +314,7 @@ async def get_account_posts(context, sort, account, start_author='', start_perml
     sql_result = await db.query_all(sql, account=account, author=start_author, permlink=start_permlink, limit=limit)
     for row in sql_result:
         post = _bridge_post_object(row)
-        post['active_votes'] = await find_votes_impl({'db':db}, row['author'], row['permlink'], VotesPresentation.BridgeApi)
+        post['active_votes'] = await find_votes_impl(db, row['author'], row['permlink'], VotesPresentation.BridgeApi)
         post = await append_statistics_to_post(post, row, False, blacklists_for_user)
         posts.append(post)
     return posts
diff --git a/hive/server/bridge_api/objects.py b/hive/server/bridge_api/objects.py
index 1f7ddbbcd..121d6d90b 100644
--- a/hive/server/bridge_api/objects.py
+++ b/hive/server/bridge_api/objects.py
@@ -100,7 +100,7 @@ async def load_posts_keyed(db, ids, truncate_body=0):
 
         row['author_rep'] = author['reputation']
         post = _bridge_post_object(row, truncate_body=truncate_body)
-        post['active_votes'] = await find_votes_impl({'db':db}, row['author'], row['permlink'], VotesPresentation.BridgeApi)
+        post['active_votes'] = await find_votes_impl(db, row['author'], row['permlink'], VotesPresentation.BridgeApi)
 
         post['blacklists'] = Mutes.lists(post['author'], author['reputation'])
 
diff --git a/hive/server/bridge_api/thread.py b/hive/server/bridge_api/thread.py
index 145534810..be6f5950d 100644
--- a/hive/server/bridge_api/thread.py
+++ b/hive/server/bridge_api/thread.py
@@ -95,7 +95,7 @@ async def get_discussion(context, author, permlink, observer=None):
     root_id = rows[0]['id']
     all_posts = {}
     root_post = _bridge_post_object(rows[0])
-    root_post['active_votes'] = await find_votes_impl({'db':db}, rows[0]['author'], rows[0]['permlink'], VotesPresentation.BridgeApi)
+    root_post['active_votes'] = await find_votes_impl(db, rows[0]['author'], rows[0]['permlink'], VotesPresentation.BridgeApi)
     root_post = await append_statistics_to_post(root_post, rows[0], False, blacklists_for_user)
     root_post['replies'] = []
     all_posts[root_id] = root_post
@@ -108,7 +108,7 @@ async def get_discussion(context, author, permlink, observer=None):
             parent_to_children_id_map[parent_id] = []
         parent_to_children_id_map[parent_id].append(rows[index]['id'])
         post = _bridge_post_object(rows[index])
-        post['active_votes'] = await find_votes_impl({'db':db}, rows[index]['author'], rows[index]['permlink'], VotesPresentation.BridgeApi)
+        post['active_votes'] = await find_votes_impl(db, rows[index]['author'], rows[index]['permlink'], VotesPresentation.BridgeApi)
         post = await append_statistics_to_post(post, rows[index], False, blacklists_for_user)
         post['replies'] = []
         all_posts[post['post_id']] = post
diff --git a/hive/server/condenser_api/methods.py b/hive/server/condenser_api/methods.py
index bf3375c65..88541517a 100644
--- a/hive/server/condenser_api/methods.py
+++ b/hive/server/condenser_api/methods.py
@@ -154,7 +154,7 @@ async def get_content(context, author: str, permlink: str, observer=None):
     if result:
         result = dict(result[0])
         post = _condenser_post_object(result, 0)
-        post['active_votes'] = await find_votes_impl(context, author, permlink, VotesPresentation.CondenserApi)
+        post['active_votes'] = await find_votes_impl(db, author, permlink, VotesPresentation.CondenserApi)
         if not observer:
             post['active_votes'] = _mute_votes(post['active_votes'], Mutes.all())
         else:
@@ -272,7 +272,7 @@ async def get_discussions_by(discussion_type, context, start_author: str = '',
     posts = []
     for row in result:
         post = _condenser_post_object(row, truncate_body)
-        post['active_votes'] = await find_votes_impl(context, post['author'], post['permlink'], VotesPresentation.DatabaseApi )
+        post['active_votes'] = await find_votes_impl(db, post['author'], post['permlink'], VotesPresentation.DatabaseApi )
         post['active_votes'] = _mute_votes(post['active_votes'], Mutes.all())
         posts.append(post)
     #posts = await resultset_to_posts(db=db, resultset=result, truncate_body=truncate_body)
@@ -381,7 +381,7 @@ async def get_discussions_by_blog(context, tag: str = None, start_author: str =
     for row in result:
         row = dict(row)
         post = _condenser_post_object(row, truncate_body=truncate_body)
-        post['active_votes'] = await find_votes_impl(context, post['author'], post['permlink'], VotesPresentation.CondenserApi)
+        post['active_votes'] = await find_votes_impl(db, post['author'], post['permlink'], VotesPresentation.CondenserApi)
         post['active_votes'] = _mute_votes(post['active_votes'], Mutes.all())
         #posts_by_id[row['post_id']] = post
         posts_by_id.append(post)
@@ -439,7 +439,7 @@ async def get_discussions_by_comments(context, start_author: str = None, start_p
     for row in result:
         row = dict(row)
         post = _condenser_post_object(row, truncate_body=truncate_body)
-        post['active_votes'] = await find_votes_impl(context, post['author'], post['permlink'], VotesPresentation.CondenserApi)
+        post['active_votes'] = await find_votes_impl(db, post['author'], post['permlink'], VotesPresentation.CondenserApi)
         post['active_votes'] = _mute_votes(post['active_votes'], Mutes.all())
         posts.append(post)
 
@@ -592,4 +592,4 @@ async def get_active_votes(context, author: str, permlink: str):
     valid_permlink(permlink)
     db = context['db']
 
-    return await find_votes_impl( {'db':db}, author, permlink, VotesPresentation.ActiveVotes  )
+    return await find_votes_impl( db, author, permlink, VotesPresentation.ActiveVotes  )
diff --git a/hive/server/condenser_api/objects.py b/hive/server/condenser_api/objects.py
index 9fef75b65..6d25aefdf 100644
--- a/hive/server/condenser_api/objects.py
+++ b/hive/server/condenser_api/objects.py
@@ -89,7 +89,7 @@ async def load_posts_keyed(db, ids, truncate_body=0):
         row['author_rep'] = author_reps[row['author']]
         post = _condenser_post_object(row, truncate_body=truncate_body)
 
-        post['active_votes'] = await find_votes_impl({'db':db}, row['author'], row['permlink'], VotesPresentation.CondenserApi)
+        post['active_votes'] = await find_votes_impl(db, row['author'], row['permlink'], VotesPresentation.CondenserApi)
         posts_by_id[row['id']] = post
 
     return posts_by_id
@@ -139,7 +139,7 @@ async def resultset_to_posts(db, resultset, truncate_body=0):
         row = dict(row)
         row['author_rep'] = author_reps[row['author']]
         post = _condenser_post_object(row, truncate_body=truncate_body)
-        post['active_votes'] = await find_votes_impl({'db':db}, row['author'], row['permlink'], VotesPresentation.CondenserApi)
+        post['active_votes'] = await find_votes_impl(db, row['author'], row['permlink'], VotesPresentation.CondenserApi)
         posts.append(post)
 
     return posts
diff --git a/hive/server/database_api/methods.py b/hive/server/database_api/methods.py
index bf906e013..473b2789d 100644
--- a/hive/server/database_api/methods.py
+++ b/hive/server/database_api/methods.py
@@ -172,7 +172,7 @@ def api_vote_info(rows, votes_presentation):
   ret = []
   for row in rows:
       if votes_presentation == VotesPresentation.DatabaseApi:
-          ret.append(dict(voter = row.voter, author = row.author, permlink = row.permlink,
+          ret.append(dict(id = row.id, voter = row.voter, author = row.author, permlink = row.permlink,
                           weight = row.weight, rshares = row.rshares, vote_percent = row.percent,
                           last_update = json_date(row.last_update), num_changes = row.num_changes))
       elif votes_presentation == VotesPresentation.CondenserApi:
@@ -188,35 +188,20 @@ def api_vote_info(rows, votes_presentation):
   return ret
 
 @return_error_info
-async def find_votes_impl(context, author: str, permlink: str, votes_presentation):
+async def find_votes_impl(db, author: str, permlink: str, votes_presentation):
+    sql = "SELECT * FROM find_votes(:author,:permlink)"
+    rows = await db.query_all(sql, author=author, permlink=permlink)
+    return api_vote_info(rows, votes_presentation)
+
+@return_error_info
+async def find_votes(context, author: str, permlink: str):
     """ Returns all votes for the given post """
     valid_account(author)
     valid_permlink(permlink)
-    db = context['db']
-    sql = """
-        SELECT
-            voter,
-            author,
-            permlink,
-            weight,
-            rshares,
-            percent,
-            last_update,
-            num_changes,
-            reputation
-        FROM
-            hive_votes_view
-        WHERE
-            author = :author AND permlink = :permlink
-        ORDER BY 
-            voter_id
-    """
-
-    rows = await db.query_all(sql, author=author, permlink=permlink)
-    return api_vote_info(rows, votes_presentation)
+    return { 'votes': await find_votes_impl(context['db'], author, permlink, VotesPresentation.DatabaseApi) }
 
 @return_error_info
-async def list_votes_impl(context, start: list, limit: int, order: str, votes_presentation):
+async def list_votes(context, start: list, limit: int, order: str):
     """ Returns all votes, starting with the specified voter and/or author and permlink. """
     supported_order_list = ["by_comment_voter", "by_voter_comment"]
     assert order in supported_order_list, "Order {} is not supported".format(order)
@@ -224,21 +209,11 @@ async def list_votes_impl(context, start: list, limit: int, order: str, votes_pr
     assert len(start) == 3, "Expecting 3 elements in start array"
     db = context['db']
 
-    sql=""
-
     if order == "by_voter_comment":
-        sql = "select * from list_votes_by_voter_comment( '{}', '{}', '{}', {} )".format( start[0], start[1], start[2], limit )
+        sql = "SELECT * FROM list_votes_by_voter_comment(:voter,:author,:permlink,:limit)"
+        rows = await db.query_all(sql, voter=start[0], author=start[1], permlink=start[2], limit=limit)
     else:
-        sql = "select * from list_votes_by_comment_voter( '{}', '{}', '{}', {} )".format( start[2], start[0], start[1], limit )
-
-    rows = await db.query_all(sql)
-
-    return api_vote_info(rows, votes_presentation)
-
-@return_error_info
-async def find_votes(context, author: str, permlink: str):
-  return await find_votes_impl( context, author, permlink, VotesPresentation.DatabaseApi)
+        sql = "SELECT * FROM list_votes_by_comment_voter(:voter,:author,:permlink,:limit)"
+        rows = await db.query_all(sql, voter=start[2], author=start[0], permlink=start[1], limit=limit)
+    return { 'votes': api_vote_info(rows, VotesPresentation.DatabaseApi) }
 
-@return_error_info
-async def list_votes(context, start: list, limit: int, order: str):
-  return await list_votes_impl( context, start, limit, order, VotesPresentation.DatabaseApi)
diff --git a/tests/tests_api b/tests/tests_api
index 4216ec21d..37ab1a0ae 160000
--- a/tests/tests_api
+++ b/tests/tests_api
@@ -1 +1 @@
-Subproject commit 4216ec21d977cb6ee08f4f655b3cf40165f7292d
+Subproject commit 37ab1a0aed778c363852c9a2b0caed5b3e06d438
-- 
GitLab