3
# Copyright 2009 Canonical Ltd. This software is licensed under the
3
# Copyright 2009-2011 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
16
14
from distutils.version import LooseVersion
15
from optparse import OptionParser
18
from optparse import OptionParser
21
19
from tempfile import NamedTemporaryFile
22
20
from textwrap import dedent
25
24
import psycopg2.extensions
25
import replication.helpers
27
27
from canonical.config import config
28
28
from canonical.database.postgresql import ConnectionString
29
29
from canonical.database.sqlbase import (
30
connect, ISOLATION_LEVEL_AUTOCOMMIT, ISOLATION_LEVEL_READ_COMMITTED,
31
quote, quote_identifier)
32
from canonical.launchpad.scripts import logger, logger_options, db_options
34
import replication.helpers
31
ISOLATION_LEVEL_AUTOCOMMIT,
32
ISOLATION_LEVEL_READ_COMMITTED,
36
from canonical.launchpad.scripts import (
36
42
# Defines parser and locale to use.
37
43
DEFAULT_CONFIG = 'default'
39
45
PGSQL_BASE = '/usr/share/postgresql'
41
A, B, C, D = 'ABCD' # tsearch2 ranking constants
47
# tsearch2 ranking constants:
43
50
# This data structure defines all of our full text indexes. Each tuple in the
44
51
# top level list creates a 'fti' column in the specified table.
224
231
# Create the trigger
225
232
columns_and_weights = []
226
233
for column, weight in qcolumns:
227
columns_and_weights.extend( (column, weight) )
234
columns_and_weights.extend((column, weight))
230
237
CREATE TRIGGER tsvectorupdate BEFORE UPDATE OR INSERT ON %s
256
263
def liverebuild(con):
257
264
"""Rebuild the data in all the fti columns against possibly live database.
259
batch_size = 50 # Update maximum of this many rows per commit
266
# Update number of rows per transaction.
260
269
cur = con.cursor()
261
270
for table, ignored in ALL_FTI:
262
271
table = quote_identifier(table)
316
325
p = subprocess.Popen(
317
326
cmd.split(' '), stdin=subprocess.PIPE,
318
327
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
328
tsearch2_sql = open(tsearch2_sql_path).read()
319
329
out, err = p.communicate(
320
"SET client_min_messages=ERROR; CREATE SCHEMA ts2;"
321
+ open(tsearch2_sql_path).read().replace('public;','ts2, public;'))
330
"SET client_min_messages=ERROR; CREATE SCHEMA ts2;" +
331
tsearch2_sql.replace('public;', 'ts2, public;'))
322
332
if p.returncode != 0:
323
log.fatal('Error executing %s:', cmd)
333
log.fatal("Error executing %s:", cmd)
335
sys.exit(p.returncode)
327
337
# Create ftq helper and its sibling _ftq.
328
338
# ftq(text) returns a tsquery, suitable for use querying the full text
363
373
# Strip ! characters inside and at the end of a word
364
374
query = re.sub(r"(?u)(?<=\w)[\!]+", " ", query)
366
# Now that we have handle case sensitive booleans, convert to lowercase
376
# Now that we have handled case-sensitive booleans, convert to
367
378
query = query.lower()
369
380
# Convert foo-bar to ((foo&bar)|foobar) and foo-bar-baz to
454
465
p = plpy.prepare("SELECT to_tsquery('%s', $1) AS x", ["text"])
455
466
query = plpy.execute(p, [query], 1)[0]["x"]
456
467
return query or None
458
469
sexecute(con, r"""
459
470
CREATE OR REPLACE FUNCTION ts2._ftq(text) RETURNS text AS %s
460
471
LANGUAGE plpythonu IMMUTABLE
569
580
We know this by looking in our cache to see what the previous
570
581
definitions were, and the --force command line argument
572
current_columns = repr(sorted(columns)) # Convert to a string
583
current_columns = repr(sorted(columns))
574
585
existing = execute(
575
586
con, "SELECT columns FROM FtiCache WHERE tablename=%(table)s",