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).
6
6
# This modules uses relative imports.
12
12
__metaclass__ = type
14
16
from distutils.version import LooseVersion
15
18
from optparse import OptionParser
19
21
from tempfile import NamedTemporaryFile
20
22
from textwrap import dedent
24
25
import psycopg2.extensions
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
25
33
import replication.helpers
27
from canonical.config import config
28
from canonical.database.postgresql import ConnectionString
29
from canonical.database.sqlbase import (
31
ISOLATION_LEVEL_AUTOCOMMIT,
32
ISOLATION_LEVEL_READ_COMMITTED,
36
from canonical.launchpad.scripts import (
42
35
# Defines parser and locale to use.
43
36
DEFAULT_CONFIG = 'default'
45
38
PGSQL_BASE = '/usr/share/postgresql'
47
# tsearch2 ranking constants:
40
A, B, C, D = 'ABCD' # tsearch2 ranking constants
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.
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) )
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.
265
# Update number of rows per transaction.
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)
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()
319
cmd += ' -h %s' % lp.dbhost
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)
334
sys.exit(p.returncode)
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)
375
# Now that we have handled case-sensitive booleans, convert to
372
# Now that we have handle case sensitive booleans, convert to lowercase
377
373
query = query.lower()
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
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
582
current_columns = repr(sorted(columns))
578
current_columns = repr(sorted(columns)) # Convert to a string
584
580
existing = execute(
585
581
con, "SELECT columns FROM FtiCache WHERE tablename=%(table)s",