pre-commit
gitea/wcs/pipeline/head This commit looks good Details

This commit is contained in:
Pierre Ducroquet 2024-03-04 11:28:05 +01:00
parent cb9a64b39d
commit 5ac8da9b4e
2 changed files with 76 additions and 29 deletions

View File

@ -692,6 +692,7 @@ class QommonPublisher(Publisher):
def clean_search_tokens(self, **kwargs):
from wcs import sql
sql.purge_obsolete_search_tokens()
@classmethod
@ -706,7 +707,9 @@ class QommonPublisher(Publisher):
cls.register_cronjob(
CronJob(cls.clean_loggederrors, hours=[3], minutes=[0], name='clean_loggederrors')
)
cls.register_cronjob(CronJob(cls.clean_search_tokens, weekdays=[0], hours=[1], minutes=[0], name='clean_tokens'))
cls.register_cronjob(
CronJob(cls.clean_search_tokens, weekdays=[0], hours=[1], minutes=[0], name='clean_tokens')
)
_initialized = False

View File

@ -95,15 +95,19 @@ SQL_TYPE_MAPPING = {
'computed': 'jsonb',
}
def _table_exists(cur, table_name):
cur.execute("SELECT 1 FROM pg_class WHERE relname = %s;", (table_name,))
cur.execute('SELECT 1 FROM pg_class WHERE relname = %s;', (table_name,))
rows = cur.fetchall()
return len(rows) > 0;
return len(rows) > 0
def _trigger_exists(cur, table_name, trigger_name):
cur.execute("SELECT 1 FROM pg_trigger WHERE tgrelid = %s::regclass AND tgname = %s;", (table_name, trigger_name))
cur.execute(
'SELECT 1 FROM pg_trigger WHERE tgrelid = %s::regclass AND tgname = %s;', (table_name, trigger_name)
)
rows = cur.fetchall()
return len(rows) > 0;
return len(rows) > 0
class WcsPgConnection(psycopg2.extensions.connection):
@ -1696,7 +1700,7 @@ def init_search_tokens(conn=None, cur=None):
conn, cur = get_connection_and_cursor()
# Create table
cur.execute("CREATE TABLE IF NOT EXISTS wcs_search_tokens(token TEXT PRIMARY KEY);")
cur.execute('CREATE TABLE IF NOT EXISTS wcs_search_tokens(token TEXT PRIMARY KEY);')
# Create triggers
init_search_tokens_triggers(cur)
@ -1705,13 +1709,16 @@ def init_search_tokens(conn=None, cur=None):
init_search_tokens_data(cur)
# Index at the end, small performance trick... not that useful, but it's free...
cur.execute("CREATE EXTENSION IF NOT EXISTS pg_trgm;")
cur.execute("CREATE INDEX IF NOT EXISTS wcs_search_tokens_trgm ON wcs_search_tokens USING gin(token gin_trgm_ops);")
cur.execute('CREATE EXTENSION IF NOT EXISTS pg_trgm;')
cur.execute(
'CREATE INDEX IF NOT EXISTS wcs_search_tokens_trgm ON wcs_search_tokens USING gin(token gin_trgm_ops);'
)
# And last: functions to use this brand new table
cur.execute("CREATE OR REPLACE AGGREGATE tsquery_agg_or (tsquery) (sfunc=tsquery_or, stype=tsquery);")
cur.execute("CREATE OR REPLACE AGGREGATE tsquery_agg_and (tsquery) (sfunc=tsquery_and, stype=tsquery);")
cur.execute("""CREATE OR REPLACE FUNCTION public.wcs_tsquery(text)
cur.execute('CREATE OR REPLACE AGGREGATE tsquery_agg_or (tsquery) (sfunc=tsquery_or, stype=tsquery);')
cur.execute('CREATE OR REPLACE AGGREGATE tsquery_agg_and (tsquery) (sfunc=tsquery_and, stype=tsquery);')
cur.execute(
r"""CREATE OR REPLACE FUNCTION public.wcs_tsquery(text)
RETURNS tsquery
LANGUAGE sql
STABLE
@ -1727,11 +1734,13 @@ with
left join wcs_search_tokens partial on partial.token % w and w not similar to '%[0-9]{2,}%'
group by w)
select tsquery_agg_and(tokens) from super_tokenized;
$function$;""")
$function$;"""
)
if own_cur:
cur.close()
def init_search_tokens_triggers(cur):
# We define only appending triggers, ie on INSERT and UPDATE.
# It would be far heavier to maintain deletions here, and having extra data has
@ -1741,7 +1750,8 @@ def init_search_tokens_triggers(cur):
# personal data is kept uselessly.
# First part: the appending function
cur.execute("""CREATE OR REPLACE FUNCTION wcs_search_tokens_trigger_fn ()
cur.execute(
"""CREATE OR REPLACE FUNCTION wcs_search_tokens_trigger_fn ()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
@ -1749,48 +1759,82 @@ BEGIN
INSERT INTO wcs_search_tokens SELECT unnest(tsvector_to_array(NEW.fts)) ON CONFLICT(token) DO NOTHING;
RETURN NEW;
END;
$function$;""")
$function$;"""
)
if not(_table_exists(cur, 'wcs_search_tokens')):
if not (_table_exists(cur, 'wcs_search_tokens')):
# abort trigger creation if tokens table doesn't exist yet
return
if _table_exists(cur, 'wcs_all_forms') and not _trigger_exists(cur, 'wcs_all_forms', 'wcs_all_forms_fts_trg_upd'):
if _table_exists(cur, 'wcs_all_forms') and not _trigger_exists(
cur, 'wcs_all_forms', 'wcs_all_forms_fts_trg_upd'
):
# Second part: insert and update triggers for wcs_all_forms
cur.execute("CREATE TRIGGER wcs_all_forms_fts_trg_ins AFTER INSERT ON wcs_all_forms FOR EACH ROW WHEN (NEW.fts IS NOT NULL) EXECUTE PROCEDURE wcs_search_tokens_trigger_fn();")
cur.execute("CREATE TRIGGER wcs_all_forms_fts_trg_upd AFTER UPDATE OF fts ON wcs_all_forms FOR EACH ROW WHEN (NEW.fts IS NOT NULL) EXECUTE PROCEDURE wcs_search_tokens_trigger_fn();")
cur.execute(
"""CREATE TRIGGER wcs_all_forms_fts_trg_ins
AFTER INSERT ON wcs_all_forms
FOR EACH ROW WHEN (NEW.fts IS NOT NULL)
EXECUTE PROCEDURE wcs_search_tokens_trigger_fn();"""
)
cur.execute(
"""CREATE TRIGGER wcs_all_forms_fts_trg_upd
AFTER UPDATE OF fts ON wcs_all_forms
FOR EACH ROW WHEN (NEW.fts IS NOT NULL)
EXECUTE PROCEDURE wcs_search_tokens_trigger_fn();"""
)
if _table_exists(cur, 'searchable_formdefs') and not _trigger_exists(cur, 'searchable_formdefs', 'searchable_formdefs_fts_trg_upd'):
if _table_exists(cur, 'searchable_formdefs') and not _trigger_exists(
cur, 'searchable_formdefs', 'searchable_formdefs_fts_trg_upd'
):
# Third part: insert and update triggers for searchable_formdefs
cur.execute("CREATE TRIGGER searchable_formdefs_fts_trg_ins AFTER INSERT ON searchable_formdefs FOR EACH ROW WHEN (NEW.fts IS NOT NULL) EXECUTE PROCEDURE wcs_search_tokens_trigger_fn();")
cur.execute("CREATE TRIGGER searchable_formdefs_fts_trg_upd AFTER UPDATE OF fts ON searchable_formdefs FOR EACH ROW WHEN (NEW.fts IS NOT NULL) EXECUTE PROCEDURE wcs_search_tokens_trigger_fn();")
cur.execute(
"""CREATE TRIGGER searchable_formdefs_fts_trg_ins
AFTER INSERT ON searchable_formdefs
FOR EACH ROW WHEN (NEW.fts IS NOT NULL)
EXECUTE PROCEDURE wcs_search_tokens_trigger_fn();"""
)
cur.execute(
"""CREATE TRIGGER searchable_formdefs_fts_trg_upd
AFTER UPDATE OF fts ON searchable_formdefs
FOR EACH ROW WHEN (NEW.fts IS NOT NULL)
EXECUTE PROCEDURE wcs_search_tokens_trigger_fn();"""
)
def init_search_tokens_data(cur):
if not(_table_exists(cur, 'wcs_search_tokens')):
if not (_table_exists(cur, 'wcs_search_tokens')):
# abort table data initialization if tokens table doesn't exist yet
return
if _table_exists(cur, 'wcs_all_forms'):
cur.execute("""INSERT INTO wcs_search_tokens
cur.execute(
"""INSERT INTO wcs_search_tokens
SELECT unnest(tsvector_to_array(fts)) FROM wcs_all_forms
ON CONFLICT(token) DO NOTHING;""")
ON CONFLICT(token) DO NOTHING;"""
)
if _table_exists(cur, 'searchable_formdefs'):
cur.execute("""INSERT INTO wcs_search_tokens
cur.execute(
"""INSERT INTO wcs_search_tokens
SELECT unnest(tsvector_to_array(fts)) FROM searchable_formdefs
ON CONFLICT(token) DO NOTHING;""")
ON CONFLICT(token) DO NOTHING;"""
)
def purge_obsolete_search_tokens(cur=None):
own_cur = False
if cur is None:
own_cur = True
conn, cur = get_connection_and_cursor()
_, cur = get_connection_and_cursor()
cur.execute("""DELETE FROM wcs_search_tokens
cur.execute(
"""DELETE FROM wcs_search_tokens
WHERE token NOT IN (SELECT unnest(tsvector_to_array(fts)) FROM wcs_all_forms)
AND token NOT IN (SELECT unnest(tsvector_to_array(fts)) FROM wcs_all_forms);""")
AND token NOT IN (SELECT unnest(tsvector_to_array(fts)) FROM wcs_all_forms);"""
)
if own_cur:
cur.close()
class SqlMixin:
_table_name = None
_numerical_id = True