~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to database/schema/fti.py

Undo rename. Again.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
#!/usr/bin/python -S
2
2
#
3
 
# Copyright 2009-2011 Canonical Ltd.  This software is licensed under the
 
3
# Copyright 2009 Canonical Ltd.  This software is licensed under the
4
4
# GNU Affero General Public License version 3 (see the file LICENSE).
5
5
#
6
6
# This modules uses relative imports.
11
11
"""
12
12
__metaclass__ = type
13
13
 
 
14
import _pythonpath
 
15
 
14
16
from distutils.version import LooseVersion
 
17
import os.path
15
18
from optparse import OptionParser
16
 
import os.path
17
19
import subprocess
18
20
import sys
19
21
from tempfile import NamedTemporaryFile
20
22
from textwrap import dedent
21
23
import time
22
24
 
23
 
import _pythonpath
24
25
import psycopg2.extensions
 
26
 
 
27
from canonical import lp
 
28
from canonical.database.sqlbase import (
 
29
    connect, ISOLATION_LEVEL_AUTOCOMMIT, ISOLATION_LEVEL_READ_COMMITTED,
 
30
    quote, quote_identifier)
 
31
from canonical.launchpad.scripts import logger, logger_options, db_options
 
32
 
25
33
import replication.helpers
26
34
 
27
 
from canonical.config import config
28
 
from canonical.database.postgresql import ConnectionString
29
 
from canonical.database.sqlbase import (
30
 
    connect,
31
 
    ISOLATION_LEVEL_AUTOCOMMIT,
32
 
    ISOLATION_LEVEL_READ_COMMITTED,
33
 
    quote,
34
 
    quote_identifier,
35
 
    )
36
 
from canonical.launchpad.scripts import (
37
 
    db_options,
38
 
    logger,
39
 
    logger_options,
40
 
    )
41
 
 
42
35
# Defines parser and locale to use.
43
36
DEFAULT_CONFIG = 'default'
44
37
 
45
38
PGSQL_BASE = '/usr/share/postgresql'
46
39
 
47
 
# tsearch2 ranking constants:
48
 
A, B, C, D = 'ABCD'
 
40
A, B, C, D = 'ABCD' # tsearch2 ranking constants
49
41
 
50
42
# This data structure defines all of our full text indexes.  Each tuple in the
51
43
# top level list creates a 'fti' column in the specified table.
65
57
 
66
58
    ('bugtask', [
67
59
            ('targetnamecache', B),
 
60
            ('statusexplanation', C),
68
61
            ]),
69
62
 
70
63
    ('binarypackagerelease', [
138
131
            ('description', D),
139
132
            ]),
140
133
 
 
134
    ('shippingrequest', [
 
135
            ('recipientdisplayname', A),
 
136
            ]),
 
137
 
141
138
    ('specification', [
142
139
            ('name', A),
143
140
            ('title', A),
230
227
    # Create the trigger
231
228
    columns_and_weights = []
232
229
    for column, weight in qcolumns:
233
 
        columns_and_weights.extend((column, weight))
 
230
        columns_and_weights.extend( (column, weight) )
234
231
 
235
232
    sql = """
236
233
        CREATE TRIGGER tsvectorupdate BEFORE UPDATE OR INSERT ON %s
262
259
def liverebuild(con):
263
260
    """Rebuild the data in all the fti columns against possibly live database.
264
261
    """
265
 
    # Update number of rows per transaction.
266
 
    batch_size = 50
267
 
 
 
262
    batch_size = 50 # Update maximum of this many rows per commit
268
263
    cur = con.cursor()
269
264
    for table, ignored in ALL_FTI:
270
265
        table = quote_identifier(table)
285
280
            except psycopg2.Error:
286
281
                # No commit - we are in autocommit mode
287
282
                log.exception('psycopg error')
288
 
                con = connect()
 
283
                con = connect(lp.dbuser)
289
284
                con.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
290
285
 
291
286
 
319
314
            """
320
315
 
321
316
        log.debug('Installing tsearch2')
322
 
        cmd = 'psql -f - %s' % ConnectionString(
323
 
            config.database.rw_main_master).asPGCommandLineArgs()
 
317
        cmd = 'psql -f - -d %s' % lp.get_dbname()
 
318
        if lp.dbhost:
 
319
            cmd += ' -h %s' % lp.dbhost
 
320
        if options.dbuser:
 
321
            cmd += ' -U %s' % options.dbuser
324
322
        p = subprocess.Popen(
325
323
            cmd.split(' '), stdin=subprocess.PIPE,
326
324
            stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
327
 
        tsearch2_sql = open(tsearch2_sql_path).read()
328
325
        out, err = p.communicate(
329
 
            "SET client_min_messages=ERROR; CREATE SCHEMA ts2;" +
330
 
            tsearch2_sql.replace('public;', 'ts2, public;'))
 
326
            "SET client_min_messages=ERROR; CREATE SCHEMA ts2;"
 
327
            + open(tsearch2_sql_path).read().replace('public;','ts2, public;'))
331
328
        if p.returncode != 0:
332
 
            log.fatal("Error executing %s:", cmd)
 
329
            log.fatal('Error executing %s:', cmd)
333
330
            log.debug(out)
334
 
            sys.exit(p.returncode)
 
331
            sys.exit(rv)
335
332
 
336
333
    # Create ftq helper and its sibling _ftq.
337
334
    # ftq(text) returns a tsquery, suitable for use querying the full text
372
369
        # Strip ! characters inside and at the end of a word
373
370
        query = re.sub(r"(?u)(?<=\w)[\!]+", " ", query)
374
371
 
375
 
        # Now that we have handled case-sensitive booleans, convert to
376
 
        # lowercase.
 
372
        # Now that we have handle case sensitive booleans, convert to lowercase
377
373
        query = query.lower()
378
374
 
379
375
        # Convert foo-bar to ((foo&bar)|foobar) and foo-bar-baz to
464
460
        p = plpy.prepare("SELECT to_tsquery('%s', $1) AS x", ["text"])
465
461
        query = plpy.execute(p, [query], 1)[0]["x"]
466
462
        return query or None
467
 
        """ % configuration
 
463
        """  % configuration
468
464
    sexecute(con, r"""
469
465
        CREATE OR REPLACE FUNCTION ts2._ftq(text) RETURNS text AS %s
470
466
        LANGUAGE plpythonu IMMUTABLE
579
575
    We know this by looking in our cache to see what the previous
580
576
    definitions were, and the --force command line argument
581
577
    '''
582
 
    current_columns = repr(sorted(columns))
 
578
    current_columns = repr(sorted(columns)) # Convert to a string
583
579
 
584
580
    existing = execute(
585
581
        con, "SELECT columns FROM FtiCache WHERE tablename=%(table)s",
631
627
# Files for output generated for slonik(1). None if not a Slony-I install.
632
628
slonik_sql = None
633
629
 
634
 
 
635
630
def main():
636
631
    parser = OptionParser()
637
632
    parser.add_option(
666
661
    global log
667
662
    log = logger(options)
668
663
 
669
 
    con = connect()
 
664
    con = connect(lp.dbuser)
670
665
 
671
666
    is_replicated_db = replication.helpers.slony_installed(con)
672
667
 
717
712
 
718
713
if __name__ == '__main__':
719
714
    sys.exit(main())
 
715
 
 
716