diff --git a/hive/db/sql_scripts/hive_post_operations.sql b/hive/db/sql_scripts/hive_post_operations.sql index 63a3d9d5b3ec7d039079ff8615f9d85c24a159fe..3cdcadaebb21195599b01655d06d4219dd2f204f 100644 --- a/hive/db/sql_scripts/hive_post_operations.sql +++ b/hive/db/sql_scripts/hive_post_operations.sql @@ -144,12 +144,13 @@ END $function$ ; -DROP FUNCTION if exists delete_hive_post(character varying,character varying,character varying, integer) +DROP FUNCTION if exists delete_hive_post(character varying,character varying,character varying, integer, timestamp) ; CREATE OR REPLACE FUNCTION delete_hive_post( in _author hive_accounts.name%TYPE, in _permlink hive_permlink_data.permlink%TYPE, - in _block_num hive_blocks.num%TYPE) + in _block_num hive_blocks.num%TYPE, + in _date hive_posts.active%TYPE) RETURNS TABLE (id hive_posts.id%TYPE, depth hive_posts.depth%TYPE) LANGUAGE plpgsql AS @@ -165,6 +166,7 @@ BEGIN WHERE ha.name = _author AND hpd.permlink = _permlink ) , block_num = _block_num + , active = _date FROM hive_posts hp1 INNER JOIN hive_accounts ha ON hp1.author_id = ha.id INNER JOIN hive_permlink_data hpd ON hp1.permlink_id = hpd.id diff --git a/hive/db/sql_scripts/utility_functions.sql b/hive/db/sql_scripts/utility_functions.sql index 896de4fe59565145d20cc5fe54be59ebc54f7754..ae326d9a0b436e81d6683c11d9e5dc144890c537 100644 --- a/hive/db/sql_scripts/utility_functions.sql +++ b/hive/db/sql_scripts/utility_functions.sql @@ -124,3 +124,27 @@ BEGIN END $function$ ; + +DROP FUNCTION IF EXISTS public.find_community_id CASCADE +; +CREATE OR REPLACE FUNCTION public.find_community_id( + in _community_name hive_communities.name%TYPE, + in _check BOOLEAN +) +RETURNS INTEGER +LANGUAGE 'plpgsql' STABLE +AS +$function$ +DECLARE + __community_id INT = 0; +BEGIN + IF (_community_name <> '') THEN + SELECT INTO __community_id COALESCE( ( SELECT id FROM hive_communities WHERE name=_community_name ), 0 ); + IF _check AND __community_id = 0 THEN + RAISE EXCEPTION 'Community % does not exist', _community_name; + END IF; + END IF; + RETURN __community_id; +END +$function$ +; diff --git a/hive/indexer/blocks.py b/hive/indexer/blocks.py index 2de572b06162d31f1c0a81a35a2c8726829c8621..89050cace91b6fc89207cbbb41f5199c9cb91ca1 100644 --- a/hive/indexer/blocks.py +++ b/hive/indexer/blocks.py @@ -266,7 +266,7 @@ class Blocks: elif op_type == 'delete_comment_operation': key = "{}/{}".format(op['author'], op['permlink']) if ( ineffective_deleted_ops is None ) or ( key not in ineffective_deleted_ops ): - Posts.delete_op(op) + Posts.delete_op(op, cls._head_block_date) elif op_type == 'comment_options_operation': Posts.comment_options_op(op) elif op_type == 'vote_operation': diff --git a/hive/indexer/posts.py b/hive/indexer/posts.py index 33ec87431464d23342551fcdd8c5aef9197403e1..609a12ae84aa350035d855ec393c1e24ab90d8d2 100644 --- a/hive/indexer/posts.py +++ b/hive/indexer/posts.py @@ -78,12 +78,12 @@ class Posts(DbAdapterHolder): cls._ids[url] = pid @classmethod - def delete_op(cls, op): + def delete_op(cls, op, block_date): """Given a delete_comment op, mark the post as deleted. Also remove it from post-cache and feed-cache. """ - cls.delete(op) + cls.delete(op, block_date) @classmethod def comment_op(cls, op, block_date): @@ -388,14 +388,14 @@ class Posts(DbAdapterHolder): beneficiaries=dumps(beneficiaries)) @classmethod - def delete(cls, op): + def delete(cls, op, block_date): """Marks a post record as being deleted.""" sql = """ SELECT id, depth - FROM delete_hive_post((:author)::varchar, (:permlink)::varchar, (:block_num)::int); + FROM delete_hive_post((:author)::varchar, (:permlink)::varchar, (:block_num)::int, (:date)::timestamp); """ - row = DB.query_row(sql, author=op['author'], permlink = op['permlink'], block_num=op['block_num']) + row = DB.query_row(sql, author=op['author'], permlink = op['permlink'], block_num=op['block_num'], date=block_date) result = dict(row) pid = result['id'] diff --git a/hive/server/bridge_api/methods.py b/hive/server/bridge_api/methods.py index c9fb5ef0ef998cc8c879806a49473e8ba5ca66c4..a2bb35e6c6b89ff372d8b3499861836b4d835dc2 100644 --- a/hive/server/bridge_api/methods.py +++ b/hive/server/bridge_api/methods.py @@ -19,8 +19,11 @@ from hive.server.common.mutes import Mutes async def get_profile(context, account, observer=None): """Load account/profile data.""" db = context['db'] + account = valid_account(account) + observer = valid_account(observer, allow_empty=True) + ret = await load_profiles(db, [valid_account(account)]) - assert ret, 'Account \'{}\' does not exist'.format(account) + assert ret, 'Account \'{}\' does not exist'.format(account) # should not be needed observer_id = await get_account_id(db, observer) if observer else None if observer_id: diff --git a/hive/server/database_api/methods.py b/hive/server/database_api/methods.py index c85de29a418727b83bbb1cf2e8fa60fd76ff368a..405da77083772d51331f15630b5be24846771d37 100644 --- a/hive/server/database_api/methods.py +++ b/hive/server/database_api/methods.py @@ -4,6 +4,7 @@ from enum import Enum from hive.server.common.helpers import return_error_info, valid_limit, valid_account, valid_permlink, valid_date from hive.server.database_api.objects import database_post_object from hive.server.common.helpers import json_date +from hive.utils.normalize import escape_characters @return_error_info async def list_comments(context, start: list, limit: int = 1000, order: str = None): @@ -140,9 +141,10 @@ async def find_comments(context, comments: list): hp.author_rewards FROM hive_posts_view hp - JOIN (VALUES {}) AS t (author, permlink) ON hp.author = t.author AND hp.permlink = t.permlink + JOIN (VALUES {}) AS t (author, permlink, number) ON hp.author = t.author AND hp.permlink = t.permlink WHERE NOT hp.is_muted + ORDER BY t.number """ idx = 0 @@ -156,7 +158,7 @@ async def find_comments(context, comments: list): continue if idx > 0: values += "," - values += "('{}','{}')".format(author, permlink) # escaping most likely needed + values += "({},{},{})".format(escape_characters(author), escape_characters(permlink), idx) idx += 1 sql = SQL_TEMPLATE.format(values) diff --git a/hive/server/hive_api/common.py b/hive/server/hive_api/common.py index dd882678996101a7617e1eb6b3e482345692ddf1..fe39d4c82e296e9ac2ae57010f56fee6e483fefa 100644 --- a/hive/server/hive_api/common.py +++ b/hive/server/hive_api/common.py @@ -14,15 +14,11 @@ def __used_refs(): async def get_community_id(db, name): """Get community id from db.""" - return await db.query_one("SELECT id FROM hive_communities WHERE name = :name", - name=name) + return await db.query_one("SELECT find_community_id( (:name)::VARCHAR, True )", name=name) async def get_account_id(db, name): """Get account id from account name.""" - assert name, 'no account name specified' - _id = await db.query_one("SELECT id FROM hive_accounts WHERE name = :n", n=name) - assert _id, "account not found: `%s`" % name - return _id + return await db.query_one("SELECT find_account_id( (:name)::VARCHAR, True )", name=name) def estimated_sp(vests): """Convert VESTS to SP units for display.""" diff --git a/hive/server/hive_api/community.py b/hive/server/hive_api/community.py index c3828d21826ebe205f5c3e78fcab0b0915afcefc..08c2e770593e538fed20d3db4ee5d68eb76e6fe8 100644 --- a/hive/server/hive_api/community.py +++ b/hive/server/hive_api/community.py @@ -35,11 +35,10 @@ async def get_community(context, name, observer=None): """ db = context['db'] cid = await get_community_id(db, name) - assert cid, 'community not found' communities = await load_communities(db, [cid], lite=False) if observer: - valid_account(observer) + observer = valid_account(observer) observer_id = await get_account_id(db, observer) await _append_observer_roles(db, communities, observer_id) await _append_observer_subs(db, communities, observer_id) @@ -50,9 +49,8 @@ async def get_community(context, name, observer=None): async def get_community_context(context, name, account): """For a community/account: returns role, title, subscribed state""" db = context['db'] - valid_account(account) + account = valid_account(account) cid = await get_community_id(db, name) - assert cid, 'community not found' aid = await get_account_id(db, account) assert aid, 'account not found' @@ -111,7 +109,7 @@ async def list_pop_communities(context, limit:int=25): async def list_all_subscriptions(context, account): """Lists all communities `account` subscribes to, plus role and title in each.""" db = context['db'] - valid_account(account) + account = valid_account(account) account_id = await get_account_id(db, account) sql = """SELECT c.name, c.title, COALESCE(r.role_id, 0), COALESCE(r.title, '') @@ -183,6 +181,7 @@ async def list_communities(context, last='', limit=100, query=None, sort='rank', # append observer context, leadership data communities = await load_communities(db, ids, lite=True) if observer: + observer = valid_account(observer) observer_id = await get_account_id(db, observer) await _append_observer_subs(db, communities, observer_id) await _append_observer_roles(db, communities, observer_id) @@ -349,11 +348,12 @@ async def top_community_authors(context, community): async def top_community_muted(context, community): """Get top authors (by SP) who are muted in a community.""" db = context['db'] + cid = await get_community_id(db, community) sql = """SELECT a.name, a.voting_weight, r.title FROM hive_accounts a JOIN hive_roles r ON a.id = r.account_id WHERE r.community_id = :community_id AND r.role_id < 0 ORDER BY voting_weight DESC LIMIT 5""" - return await db.query(sql, community_id=await get_community_id(db, community)) + return await db.query(sql, community_id=cid) async def _top_community_posts(db, community, limit=50): # TODO: muted equivalent diff --git a/hive/server/hive_api/objects.py b/hive/server/hive_api/objects.py index f6bb342f9e97ef917dcdd53e31c4444ef172bb44..560269c8780d9574f34f42a4eb7ef603ad3c370f 100644 --- a/hive/server/hive_api/objects.py +++ b/hive/server/hive_api/objects.py @@ -36,6 +36,7 @@ async def accounts_by_name(db, names, observer=None, lite=True): accounts[account['id']] = account if observer: + observer = valid_account(observer) await _follow_contexts(db, accounts, observer_id=await get_account_id(db, observer), include_mute=not lite) diff --git a/hive/server/hive_api/public.py b/hive/server/hive_api/public.py index 273607a56d1e9bb5c97c4bbdcda1afbd8ceb0ae8..0009453caec02d25700e01625884f8c2a65590ea 100644 --- a/hive/server/hive_api/public.py +++ b/hive/server/hive_api/public.py @@ -58,6 +58,7 @@ async def list_following(context, account:str, start:str='', limit:int=50, obser async def list_all_muted(context, account): """Get a list of all account names muted by `account`.""" db = context['db'] + account = valid_account(account) sql = """SELECT a.name FROM hive_follows f JOIN hive_accounts a ON f.following_id = a.id WHERE follower = :follower AND state = 2""" diff --git a/hive/utils/post_active.py b/hive/utils/post_active.py index 99e37c4700879d174cdf0a27bbd37f4369bffb66..86a272aca2d10bf5023db0230cf311ccb82c7dab 100644 --- a/hive/utils/post_active.py +++ b/hive/utils/post_active.py @@ -6,54 +6,55 @@ DB = Db.instance() There are three cases when 'active' field in post is updated: 1) when a descendant post comment was added (recursivly on any depth) 2) when a descendant post comment was deleted (recursivly on any depth) -3) when the post is updated +3) when the post is updated - that one only updates that post active (not here) It means that, when the comment for posts is updated then its 'active' field does not propagate for its ancestors. """ update_active_sql = """ - WITH RECURSIVE parent_posts ( parent_id, post_id, intrusive_active) AS ( - SELECT - parent_id as parent_id, - id as post_id, - CASE WHEN hp1.active = hp1.created_at OR hp1.counter_deleted > 0 THEN hp1.active - ELSE hp1.created_at - END as intrusive_active - FROM hive_posts hp1 {} - UNION - SELECT - hp2.parent_id as parent_id, - id as post_id, - max_time_stamp( - CASE WHEN hp2.active = hp2.created_at OR hp2.counter_deleted > 0 THEN hp2.active - ELSE hp2.created_at - END - , pp.intrusive_active - ) as intrusive_active - FROM parent_posts pp - JOIN hive_posts hp2 ON pp.parent_id = hp2.id - WHERE hp2.depth > 0 AND pp.intrusive_active > hp2.active + WITH RECURSIVE parent_posts ( parent_id, post_id, intrusive_active ) AS ( + SELECT + hp1.parent_id as parent_id, + hp1.id as post_id, + CASE WHEN hp1.counter_deleted > 0 THEN hp1.active + ELSE hp1.created_at + END as intrusive_active + FROM hive_posts hp1 + WHERE hp1.depth > 0 {} + UNION + SELECT + hp2.parent_id as parent_id, + hp2.id as post_id, + max_time_stamp( + CASE WHEN hp2.counter_deleted > 0 THEN hp2.active + ELSE hp2.created_at + END + , pp.intrusive_active + ) as intrusive_active + FROM parent_posts pp + JOIN hive_posts hp2 ON pp.parent_id = hp2.id + WHERE hp2.depth > 0 ) - UPDATE - hive_posts - SET - active = new_active - FROM - ( - SELECT hp.id as post_id, max_time_stamp( hp.active, MAX(pp.intrusive_active)) as new_active - FROM parent_posts pp - JOIN hive_posts hp ON pp.parent_id = hp.id GROUP BY hp.id + UPDATE + hive_posts + SET + active = new_active + FROM + ( + SELECT hp.id as post_id, max_time_stamp( hp.active, MAX(pp.intrusive_active) ) as new_active + FROM parent_posts pp + JOIN hive_posts hp ON pp.parent_id = hp.id GROUP BY hp.id ) as dataset WHERE dataset.post_id = hive_posts.id; """ def update_all_posts_active(): - DB.query_no_return(update_active_sql.format( "WHERE ( children = 0 OR hp1.counter_deleted > 0 ) AND depth > 0" )) + DB.query_no_return(update_active_sql.format( "AND ( hp1.children = 0 )" )) @time_it def update_active_starting_from_posts_on_block( first_block_num, last_block_num ): if first_block_num == last_block_num: - DB.query_no_return(update_active_sql.format( "WHERE block_num={} AND depth > 0" ).format(first_block_num) ) + DB.query_no_return(update_active_sql.format( "AND hp1.block_num = {}" ).format(first_block_num) ) return - DB.query_no_return(update_active_sql.format( "WHERE block_num>={} AND block_num <={} AND depth > 0" ).format(first_block_num, last_block_num) ) + DB.query_no_return(update_active_sql.format( "AND hp1.block_num >= {} AND hp1.block_num <= {}" ).format(first_block_num, last_block_num) ) diff --git a/tests/tests_api b/tests/tests_api index 10288df4f5217ee9c38b5fb8e8c0b916500a7df9..70dd4c0e465e2443ae4c1cb48d7e7dc29341f2dc 160000 --- a/tests/tests_api +++ b/tests/tests_api @@ -1 +1 @@ -Subproject commit 10288df4f5217ee9c38b5fb8e8c0b916500a7df9 +Subproject commit 70dd4c0e465e2443ae4c1cb48d7e7dc29341f2dc