incubator-bloodhound-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From j...@apache.org
Subject svn commit: r1420075 - in /incubator/bloodhound/branches/bep_0003_multiproduct: bloodhound_multiproduct/multiproduct/api.py bloodhound_multiproduct/multiproduct/model.py trac/trac/bloodhound.py trac/trac/bloodhoundsql.py trac/trac/web/standalone.py
Date Tue, 11 Dec 2012 10:28:07 GMT
Author: jure
Date: Tue Dec 11 10:28:05 2012
New Revision: 1420075

URL: http://svn.apache.org/viewvc?rev=1420075&view=rev
Log:
initial SQL proxy patch as described in ticket #288


Added:
    incubator/bloodhound/branches/bep_0003_multiproduct/trac/trac/bloodhound.py
    incubator/bloodhound/branches/bep_0003_multiproduct/trac/trac/bloodhoundsql.py
Modified:
    incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_multiproduct/multiproduct/api.py
    incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_multiproduct/multiproduct/model.py
    incubator/bloodhound/branches/bep_0003_multiproduct/trac/trac/web/standalone.py

Modified: incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_multiproduct/multiproduct/api.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_multiproduct/multiproduct/api.py?rev=1420075&r1=1420074&r2=1420075&view=diff
==============================================================================
--- incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_multiproduct/multiproduct/api.py
(original)
+++ incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_multiproduct/multiproduct/api.py
Tue Dec 11 10:28:05 2012
@@ -23,7 +23,7 @@ from genshi.builder import tag
 
 from pkg_resources import resource_filename
 from trac.core import Component, TracError, implements
-from trac.db import Table, Column, DatabaseManager
+from trac.db import Table, Column, DatabaseManager, Index
 from trac.env import IEnvironmentSetupParticipant
 from trac.perm import IPermissionRequestor
 from trac.resource import IResourceManager
@@ -33,7 +33,7 @@ from trac.web.chrome import ITemplatePro
 
 from multiproduct.model import Product
 
-DB_VERSION = 2
+DB_VERSION = 3
 DB_SYSTEM_KEY = 'bloodhound_multi_product_version'
 PLUGIN_NAME = 'Bloodhound multi product'
 
@@ -114,7 +114,89 @@ class MultiProductSystem(Component):
                       WHERE name=%s""", (DB_VERSION, DB_SYSTEM_KEY))
                 self.log.info("Upgraded multiproduct db schema from version %d"
                               " to %d" % (db_installed_version, DB_VERSION))
-    
+
+            if db_installed_version == 2:
+                from trac import bloodhound
+                migrate_tables = ['enum', 'component', 'milestone', 'version', 'wiki']
+                table_defs = [
+                    Table('enum', key=('type', 'name', 'product'))[
+                        Column('type'),
+                        Column('name'),
+                        Column('value'),
+                        Column('product')],
+                    Table('component', key=('name', 'product'))[
+                        Column('name'),
+                        Column('owner'),
+                        Column('description'),
+                        Column('product')],
+                    Table('milestone', key=('name', 'product'))[
+                        Column('name'),
+                        Column('due', type='int64'),
+                        Column('completed', type='int64'),
+                        Column('description'),
+                        Column('product')],
+                    Table('version', key=('name', 'product'))[
+                        Column('name'),
+                        Column('time', type='int64'),
+                        Column('description'),
+                        Column('product')],
+                    Table('wiki', key=('name', 'version', 'product'))[
+                        Column('name'),
+                        Column('version', type='int'),
+                        Column('time', type='int64'),
+                        Column('author'),
+                        Column('ipnr'),
+                        Column('text'),
+                        Column('comment'),
+                        Column('readonly', type='int'),
+                        Column('product'),
+                        Index(['time'])],
+                    ]
+                table_columns = dict()
+                table_vals = {}
+                for table in table_defs:
+                    table_columns[table.name] = filter(lambda column: column != 'product',
[column.name for column in list(filter(lambda t: t.name == table.name, table_defs)[0].columns)])
+                table_columns['bloodhound_product'] = ['prefix', 'name', 'description', 'owner']
+                def fetch_table(table):
+                    table_vals[table] = list(db("SELECT %s FROM %s" % (','.join(table_columns[table]),
table)))
+                for table in table_columns.keys():
+                    self.log.info("Fetching table '%s'", table)
+                    fetch_table(table)
+                for table in migrate_tables:
+                    self.log.info("Dropping obsolete table '%s'", table)
+                    db("DROP TABLE %s" % table)
+                db_connector, _ = DatabaseManager(self.env).get_connector()
+                for table in table_defs:
+                    self.log.info("Creating table '%s'", table.name)
+                    for sql in db_connector.to_sql(table):
+                        db(sql)
+                self.log.info("Creating default product")
+                db("INSERT INTO bloodhound_product (prefix, name, description, owner) VALUES
('%s', 'Default', 'Default product', '')" % bloodhound.DEFAULT_PRODUCT)
+                self.log.info("Migrating tickets w/o product to default product")
+                db("UPDATE ticket SET product='%s' WHERE product=''" % bloodhound.DEFAULT_PRODUCT)
+
+                def insert_with_product(table, product):
+                    cols = table_columns[table] + ['product']
+                    sql = "INSERT INTO %s (%s) VALUES (%s)" % (table,
+                                                               ','.join(cols),
+                                                               ','.join(['%s'] * len(cols)))
+                    for r in table_vals[table]:
+                        vals = list()
+                        for v in list(r):
+                            vals.append(v if v else '')
+                        db(sql, tuple(vals + [product]))
+                for p in table_vals['bloodhound_product']:
+                    for table in migrate_tables:
+                        self.log.info("Creating tables '%s' for default product", table)
+                        insert_with_product(table, bloodhound.DEFAULT_PRODUCT)
+                        self.log.info("Creating tables '%s' for product '%s' ('%s')", table,
p[1], p[0])
+                        insert_with_product(table, p[0])
+
+                db("""UPDATE system SET value=%s
+                      WHERE name=%s""", (DB_VERSION, DB_SYSTEM_KEY))
+                self.log.info("Upgraded multiproduct db schema from version %d"
+                              " to %d" % (db_installed_version, DB_VERSION))
+
     # ITemplateProvider methods
     def get_templates_dirs(self):
         """provide the plugin templates"""

Modified: incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_multiproduct/multiproduct/model.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_multiproduct/multiproduct/model.py?rev=1420075&r1=1420074&r2=1420075&view=diff
==============================================================================
--- incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_multiproduct/multiproduct/model.py
(original)
+++ incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_multiproduct/multiproduct/model.py
Tue Dec 11 10:28:05 2012
@@ -283,6 +283,20 @@ class Product(ModelBase):
         q = Query.from_string(env, 'product=%s' % product)
         return q.execute()
 
+    def insert(self):
+        from trac import db_default
+        from trac.bloodhound import  TRANSLATE_TABLES
+        with self._env.db_transaction as db:
+            super(Product, self).insert()
+            for table, cols, vals in db_default.get_data(db):
+                if not table in TRANSLATE_TABLES:
+                    continue
+                self._env.product_aware = False
+                self._env.product_scope = self.prefix
+                db.executemany("INSERT INTO %s (%s) VALUES (%s)" % (table,
+                                                                    ','.join(cols),
+                                                                    ','.join(['%s'] * len(cols))),
vals)
+
 class ProductResourceMap(ModelBase):
     """Table representing the mapping of resources to their product"""
     _meta = {'table_name':'bloodhound_productresourcemap',

Added: incubator/bloodhound/branches/bep_0003_multiproduct/trac/trac/bloodhound.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/branches/bep_0003_multiproduct/trac/trac/bloodhound.py?rev=1420075&view=auto
==============================================================================
--- incubator/bloodhound/branches/bep_0003_multiproduct/trac/trac/bloodhound.py (added)
+++ incubator/bloodhound/branches/bep_0003_multiproduct/trac/trac/bloodhound.py Tue Dec 11
10:28:05 2012
@@ -0,0 +1,95 @@
+
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.
+
+import trac.env
+from trac.db.util import IterableCursor
+from trac.util import concurrency
+from trac.env import Environment
+from bloodhoundsql import BloodhoundProductSQLTranslate
+
+DEFAULT_PRODUCT = 'default'
+TRANSLATE_TABLES = ['ticket', 'enum', 'component', 'milestone', 'version', 'wiki']
+PRODUCT_COLUMN = 'product'
+
+class BloodhoundIterableCursor(IterableCursor):
+    __slots__ = IterableCursor.__slots__ + ['_translator']
+    _tls = concurrency.ThreadLocal(env=None)
+
+    def __init__(self, cursor, log=None):
+        super(BloodhoundIterableCursor, self).__init__(cursor, log=log)
+        self._translator = None
+
+    @property
+    def translator(self):
+        if not self._translator:
+            product = self.env.product_scope if self.env else DEFAULT_PRODUCT
+            self._translator = BloodhoundProductSQLTranslate(TRANSLATE_TABLES,
+                                                             PRODUCT_COLUMN,
+                                                             product)
+        return self._translator
+
+    def _translate_sql(self, sql):
+        return self.translator.translate(sql) if (self.env and not self.env.product_aware)
else sql
+
+    def execute(self, sql, args=None):
+        return super(BloodhoundIterableCursor, self).execute(self._translate_sql(sql), args=args)
+
+    def executemany(self, sql, args=None):
+        return super(BloodhoundIterableCursor, self).executemany(self._translate_sql(sql),
args=args)
+
+    @property
+    def env(self):
+        return self._tls.env
+
+    @classmethod
+    def set_env(cls, env):
+        cls._tls.env = env
+
+class BloodhoundEnvironment(Environment):
+    def __init__(self, path, create=False, options=[]):
+        super(BloodhoundEnvironment, self).__init__(path, create=create, options=options)
+        self._product_scope = DEFAULT_PRODUCT
+        self._product_aware = False
+
+    @property
+    def product_scope(self):
+        return self._product_scope
+    @product_scope.setter
+    def product_scope(self, value):
+        self._product_scope = value
+
+    @property
+    def product_aware(self):
+        return self._product_aware
+    @product_aware.setter
+    def product_aware(self, value):
+        self._product_aware = value
+
+    @property
+    def db_query(self):
+        BloodhoundIterableCursor.set_env(self)
+        return super(BloodhoundEnvironment, self).db_query
+
+    @property
+    def db_transaction(self):
+        BloodhoundIterableCursor.set_env(self)
+        return super(BloodhoundEnvironment, self).db_transaction
+
+def bloodhound_hooks():
+    trac.env.Environment = BloodhoundEnvironment
+    trac.db.util.IterableCursor = BloodhoundIterableCursor

Added: incubator/bloodhound/branches/bep_0003_multiproduct/trac/trac/bloodhoundsql.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/branches/bep_0003_multiproduct/trac/trac/bloodhoundsql.py?rev=1420075&view=auto
==============================================================================
--- incubator/bloodhound/branches/bep_0003_multiproduct/trac/trac/bloodhoundsql.py (added)
+++ incubator/bloodhound/branches/bep_0003_multiproduct/trac/trac/bloodhoundsql.py Tue Dec
11 10:28:05 2012
@@ -0,0 +1,397 @@
+
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.
+
+import sqlparse
+import sqlparse.tokens as Tokens
+import sqlparse.sql as Types
+
+__all__ = ['BloodhoundProductSQLTranslate']
+
+class BloodhoundProductSQLTranslate(object):
+    _join_statements = ['LEFT JOIN', 'LEFT OUTER JOIN',
+                        'RIGHT JOIN', 'RIGHT OUTER JOIN',
+                        'JOIN', 'INNER JOIN']
+    _from_end_words = ['WHERE', 'GROUP', 'HAVING', 'ORDER', 'UNION']
+
+    def __init__(self, translate_tables, product_column, product_prefix):
+        self._translate_tables = translate_tables
+        self._product_column = product_column
+        self._product_prefix = product_prefix
+
+    def _select_table_name_alias(self, tokens):
+        return filter(lambda t: t.upper() != 'AS', [t.value for t in tokens if t.value.strip()])
+    def _column_expression_name_alias(self, tokens):
+        return filter(lambda t: t.upper() != 'AS', [t.value for t in tokens if t.value.strip()])
+
+    def _patch_table_view_sql(self, name, alias=None):
+        sql = '(SELECT * FROM %s WHERE %s="%s")' % (name, self._product_column, self._product_prefix)
+        if alias:
+            sql += ' AS %s' % alias
+        return sql
+
+    def _token_first(self, parent):
+        return parent.token_first()
+    def _token_next_match(self, parent, start_token, ttype, token):
+        return parent.token_next_match(self._token_idx(parent, start_token), ttype, token)
+    def _token_next(self, parent, from_token):
+        return parent.token_next(self._token_idx(parent, from_token))
+    def _token_prev(self, parent, from_token):
+        return parent.token_prev(self._token_idx(parent, from_token))
+    def _token_next_by_instance(self, parent, start_token, klass):
+        return parent.token_next_by_instance(self._token_idx(parent, start_token), klass)
+    def _token_next_by_type(self, parent, start_token, ttype):
+        return parent.token_next_by_type(self._token_idx(parent, start_token), ttype)
+    def _token_insert_before(self, parent, where, token):
+        return parent.insert_before(where, token)
+    def _token_insert_after(self, parent, where, token):
+        return parent.insert_after(where, token)
+    def _token_idx(self, parent, token):
+        return parent.token_index(token)
+
+    def _eval_expression_value(self, parent, token):
+        if isinstance(token, Types.Parenthesis):
+            t = self._token_first(token)
+            if t.match(Tokens.Punctuation, '('):
+                t = self._token_next(token, t)
+                if t.match(Tokens.DML, 'SELECT'):
+                    self._select(token, t)
+
+    def _expression_token_unwind_hack(self, parent, token, start_token):
+        # hack to workaround sqlparse bug that wrongly presents list of tokens
+        # as IdentifierList in certain situations
+        if isinstance(token, Types.IdentifierList):
+            expression_token_idx = self._token_idx(parent, token)
+            del parent.tokens[expression_token_idx]
+            last_token = start_token
+            for t in token.tokens:
+                self._token_insert_after(parent, last_token, t)
+                last_token = t
+            token = self._token_next(parent, start_token)
+        return token
+
+    def _where(self, parent, where_token):
+        if isinstance(where_token, Types.Where):
+            token = self._token_first(where_token)
+            if not token.match(Tokens.Keyword, 'WHERE'):
+                raise Exception("Invalid WHERE statement")
+            while token:
+                self._eval_expression_value(where_token, token)
+                token = self._token_next(where_token, token)
+        return
+
+    def _select_expression_tokens(self, parent, first_token, end_words):
+        if isinstance(first_token, Types.IdentifierList):
+            return first_token, [first_token]
+        tokens = list()
+        current_list = list()
+        current_token = first_token
+        while current_token and not current_token.match(Tokens.Keyword, end_words):
+            if current_token.match(Tokens.Punctuation, ','):
+                if current_list:
+                    tokens.append(current_list)
+                    current_list = list()
+            elif current_token.is_whitespace():
+                pass
+            else:
+                current_list.append(current_token)
+            current_token = self._token_next(parent, current_token)
+        if current_list:
+            tokens.append(current_list)
+        return current_token, tokens
+
+    def _select_join(self, parent, start_token, end_words):
+        current_token = self._select_from(parent, start_token, ['ON'])
+        tokens = list()
+        if current_token:
+            current_token = self._token_next(parent, current_token)
+            while current_token and\
+                  not current_token.match(Tokens.Keyword, end_words) and\
+                  not isinstance(current_token, Types.Where):
+                tokens.append(current_token)
+                current_token = self._token_next(parent, current_token)
+        return current_token
+
+    def _select_from(self, parent, start_token, end_words, table_name_callback=None):
+        def inject_table_view(token, name, alias):
+            parent.tokens[self._token_idx(parent, token)] = sqlparse.parse(self._patch_table_view_sql(name,
+                                                                                        
             alias=alias))[0]
+
+        def process_table_name_tokens(token, nametokens):
+            if nametokens:
+                l = self._select_table_name_alias(nametokens)
+                if not l:
+                    raise Exception("Invalid FROM table name")
+                name, alias = l[0], None
+                if len(l) > 1:
+                    alias = l[1]
+                if name in self._translate_tables:
+                    inject_table_view(token, name, alias)
+                if table_name_callback:
+                    table_name_callback(tablename)
+            return list()
+
+        current_token = self._token_next(parent, start_token)
+        last_token = current_token
+        table_name_tokens = list()
+        join_tokens = list()
+        while current_token and \
+              not current_token.match(Tokens.Keyword, end_words) and \
+              not isinstance(current_token, Types.Where):
+            last_token = current_token
+            next_token = self._token_next(parent, current_token)
+            if current_token.is_whitespace():
+                pass
+            elif isinstance(current_token, Types.Identifier):
+                parenthesis = filter(lambda t: isinstance(t, Types.Parenthesis), current_token.tokens)
+                if parenthesis:
+                    for p in parenthesis:
+                        t = self._token_next(p, p.token_first())
+                        if not t.match(Tokens.DML, 'SELECT'):
+                            raise Exception("Invalid subselect statement")
+                        self._select(p, t)
+                else:
+                    tablename = current_token.value.strip()
+                    tablealias = current_token.get_name().strip()
+                    if tablename in self._translate_tables:
+                        inject_table_view(current_token, tablename, tablealias)
+                    if table_name_callback:
+                        table_name_callback(tablename)
+            elif current_token.ttype == Tokens.Punctuation:
+                if table_name_tokens:
+                    next_token = self._token_next(parent, current_token)
+                    table_name_tokens = process_table_name_tokens(current_token,
+                                                                  table_name_tokens)
+            elif current_token.match(Tokens.Keyword, ['JOIN', 'LEFT', 'RIGHT', 'INNER', 'OUTER']
+ self._join_statements):
+                join_tokens.append(current_token.value.strip().upper())
+                join = ' '.join(join_tokens)
+                if join in self._join_statements:
+                    join_tokens = list()
+                    next_token = self._select_join(parent,
+                                                   current_token,
+                                                   ['JOIN', 'LEFT', 'RIGHT', 'INNER', 'OUTER']
+                                                    + self._join_statements
+                                                    + self._from_end_words)
+            elif current_token.ttype == Tokens.Keyword or \
+                 current_token.ttype == Tokens.Token.Literal.Number.Integer:
+                table_name_tokens.append(current_token)
+            else:
+                raise Exception("Failed to parse FROM table name")
+            current_token = next_token
+
+        if last_token and table_name_tokens:
+            process_table_name_tokens(last_token,
+                                      table_name_tokens)
+        return current_token
+
+    def _select(self, parent, start_token, insert_table=None):
+        token = self._token_next(parent, start_token)
+        fields_token = self._token_next(parent, token) if token.match(Tokens.Keyword, ['ALL',
'DISTINCT']) else token
+        current_token, field_lists = self._select_expression_tokens(parent, fields_token,
['FROM'] + self._from_end_words)
+        def handle_insert_table(table_name):
+            if table_name == insert_table:
+                for keyword in [self._product_column, ',', ' ']:
+                    self._token_insert_before(parent, fields_token, Types.Token(Tokens.Keyword,
keyword))
+            return
+        table_name_callback = handle_insert_table if insert_table else None
+        from_token = self._token_next_match(parent, start_token, Tokens.Keyword, 'FROM')
+        if not from_token:
+            raise Exception("Expected FROM in SELECT")
+        current_token = self._select_from(parent,
+                                          from_token, self._from_end_words,
+                                          table_name_callback=table_name_callback)
+        if not current_token:
+            return None
+        while current_token:
+            if isinstance(current_token, Types.Where) or \
+               current_token.match(Tokens.Keyword, ['GROUP', 'HAVING', 'ORDER']):
+                if isinstance(current_token, Types.Where):
+                    self._where(parent, current_token)
+                start_token = self._token_next(parent, current_token)
+                next_token = self._token_next_match(parent,
+                                                    start_token,
+                                                    Tokens.Keyword,
+                                                    self._from_end_words) if start_token
else None
+            elif current_token.match(Tokens.Keyword, ['UNION']):
+                token = self._token_next(parent, current_token)
+                if not token.match(Tokens.DML, 'SELECT'):
+                    raise Exception("Invalid SELECT UNION statement")
+                token = self._select(parent, current_token, insert_table=insert_table)
+                next_token = self._token_next(parent, token) if token else None
+            else:
+                raise Exception("Unsupported SQL statement")
+            current_token = next_token
+        return current_token
+
+    def _insert(self, parent, start_token):
+        token = self._token_next(parent, start_token)
+        if not token.match(Tokens.Keyword, 'INTO'):
+            raise Exception("Invalid INSERT statement")
+        def insert_extra_column(tablename, columns_token):
+            if tablename in self._translate_tables and\
+               isinstance(columns_token, Types.Parenthesis):
+                ptoken = self._token_first(columns_token)
+                if not ptoken.match(Tokens.Punctuation, '('):
+                    raise Exception("Invalid INSERT statement")
+                for keyword in [' ', ',', self._product_column]:
+                    self._token_insert_after(columns_token, ptoken, Types.Token(Tokens.Keyword,
keyword))
+            return
+        def insert_extra_column_value(tablename, ptoken, start_token):
+            if tablename in self._translate_tables:
+                for keyword in [',', "'", self._product_prefix, "'"]:
+                    self._token_insert_after(ptoken, start_token, Types.Token(Tokens.Keyword,
keyword))
+            return
+        tablename = None
+        table_name_token = self._token_next(parent, token)
+        if isinstance(table_name_token, Types.Function):
+            token = self._token_first(table_name_token)
+            if isinstance(token, Types.Identifier):
+                tablename = token.get_name()
+                columns_token = self._token_next(table_name_token, token)
+                insert_extra_column(tablename, columns_token)
+                token = self._token_next(parent, table_name_token)
+        else:
+            tablename = table_name_token.value
+            columns_token = self._token_next(parent, table_name_token)
+            insert_extra_column(tablename, columns_token)
+            token = self._token_next(parent, columns_token)
+        if token.match(Tokens.Keyword, 'VALUES'):
+            token = self._token_next(parent, token)
+            while token:
+                if isinstance(token, Types.Parenthesis):
+                    ptoken = self._token_first(token)
+                    if not ptoken.match(Tokens.Punctuation, '('):
+                        raise Exception("Invalid INSERT statement")
+                    insert_extra_column_value(tablename, token, ptoken)
+                    while ptoken:
+                        if not ptoken.match(Tokens.Punctuation, [',', '(', ')']) and \
+                           not ptoken.match(Tokens.Keyword, [',', '(', ')']) and \
+                           not ptoken.is_whitespace():
+                            ptoken = self._expression_token_unwind_hack(token, ptoken, self._token_prev(token,
ptoken))
+                            self._eval_expression_value(token, ptoken)
+                        ptoken = self._token_next(token, ptoken)
+                elif not token.match(Tokens.Punctuation, [',', '(', ')']) and\
+                     not token.match(Tokens.Keyword, [',', '(', ')']) and\
+                     not token.is_whitespace():
+                    raise Exception("Invalid INSERT statement, unable to parse VALUES section")
+                token = self._token_next(parent, token)
+        elif token.match(Tokens.DML, 'SELECT'):
+            self._select(parent, token, insert_table=tablename)
+        else:
+            raise Exception("Invalid INSERT statement")
+        return
+
+    def _update_delete_where_limit(self, table_name, parent, start_token):
+        if not start_token:
+            return
+        where_token = start_token if isinstance(start_token, Types.Where) \
+                                  else self._token_next_by_instance(parent, start_token,
Types.Where)
+        if where_token:
+            self._where(parent, where_token)
+        if not table_name in self._translate_tables:
+            return
+        if where_token:
+            keywords = [self._product_column, '=', "'", self._product_prefix, "'", ' ', 'AND',
' ']
+            keywords.reverse()
+            token = self._token_first(where_token)
+            if not token.match(Tokens.Keyword, 'WHERE'):
+                token = self._token_next_match(where_token, token, Tokens.Keyword, 'WHERE')
+            if not token:
+                raise Exception("Invalid UPDATE statement, failed to parse WHERE")
+            for keyword in keywords:
+                self._token_insert_after(where_token, token, Types.Token(Tokens.Keyword,
keyword))
+        else:
+            keywords = ['WHERE', ' ', self._product_column, '=', "'", self._product_prefix,
"'"]
+            limit_token = self._token_next_match(parent, start_token, Tokens.Keyword, 'LIMIT')
+            if limit_token:
+                for keyword in keywords:
+                    self._token_insert_before(parent, limit_token, Types.Token(Tokens.Keyword,
keyword))
+                self._token_insert_before(parent, limit_token, Types.Token(Tokens.Keyword,
' '))
+            else:
+                last_token = token = start_token
+                while token:
+                    last_token = token
+                    token = self._token_next(parent, token)
+                keywords.reverse()
+                for keyword in keywords:
+                    self._token_insert_after(parent, last_token, Types.Token(Tokens.Keyword,
keyword))
+        return
+
+    def _update(self, parent, start_token):
+        table_name_token = self._token_next(parent, start_token)
+        if isinstance(table_name_token, Types.Identifier):
+            tablename = table_name_token.get_name()
+        elif isinstance(table_name_token, Types.Token):
+            tablename = table_name_token.value
+        else:
+            raise Exception("Invalid UPDATE statement, expected table name")
+        set_token = self._token_next_match(parent, table_name_token, Tokens.Keyword, 'SET')
+        if set_token:
+            token = set_token
+            while token and \
+                  not isinstance(token, Types.Where) and \
+                  not token.match(Tokens.Keyword, 'LIMIT'):
+                if not token.match(Tokens.Keyword, 'SET') and \
+                   not token.match(Tokens.Punctuation, ','):
+                    raise Exception("Invalid UPDATE statement, failed to match separator")
+                column_token = self._token_next(parent, token)
+                if isinstance(column_token, Types.Comparison):
+                    token = self._token_next(parent, column_token)
+                    continue
+                equals_token = self._token_next(parent, column_token)
+                if not equals_token.match(Tokens.Token.Operator.Comparison, '='):
+                    raise Exception("Invalid UPDATE statement, SET equals token mismatch")
+                expression_token = self._token_next(parent, equals_token)
+                expression_token = self._expression_token_unwind_hack(parent, expression_token,
equals_token)
+                self._eval_expression_value(parent, expression_token)
+                token = self._token_next(parent, expression_token)
+            start_token = token
+        self._update_delete_where_limit(tablename, parent, start_token)
+        return
+
+    def _delete(self, parent, start_token):
+        token = self._token_next(parent, start_token)
+        if not token.match(Tokens.Keyword, 'FROM'):
+            raise Exception("Invalid DELETE statement")
+        table_name_token = self._token_next(parent, token)
+        if isinstance(table_name_token, Types.Identifier):
+            tablename = table_name_token.get_name()
+        elif isinstance(table_name_token, Types.Token):
+            tablename = table_name_token.value
+        else:
+            raise Exception("Invalid DELETE statement, expected table name")
+        if not tablename in self._translate_tables:
+            return
+        self._update_delete_where_limit(tablename, parent, start_token)
+        return
+
+    def translate(self, sql):
+        dml_handlers = {'SELECT': self._select,
+                        'INSERT': self._insert,
+                        'UPDATE': self._update,
+                        'DELETE': self._delete,
+                        }
+        try:
+            sql_statement = sqlparse.parse(sql)[0]
+            t = sql_statement.token_first()
+            if not t.match(Tokens.DML, dml_handlers.keys()):
+                return sql
+            dml_handlers[t.value](sql_statement, t)
+            translated_sql = sqlparse.format(sql_statement.to_unicode(), reindent=True)
+        except Exception:
+            raise Exception("Failed to translate SQL '%s'" % sql)
+        return translated_sql

Modified: incubator/bloodhound/branches/bep_0003_multiproduct/trac/trac/web/standalone.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/branches/bep_0003_multiproduct/trac/trac/web/standalone.py?rev=1420075&r1=1420074&r2=1420075&view=diff
==============================================================================
--- incubator/bloodhound/branches/bep_0003_multiproduct/trac/trac/web/standalone.py (original)
+++ incubator/bloodhound/branches/bep_0003_multiproduct/trac/trac/web/standalone.py Tue Dec
11 10:28:05 2012
@@ -120,6 +120,8 @@ class TracHTTPRequestHandler(WSGIRequest
 class TracHTTP11RequestHandler(TracHTTPRequestHandler):
     protocol_version = 'HTTP/1.1'
 
+from trac.bloodhound import bloodhound_hooks
+bloodhound_hooks()
 
 def main():
     from optparse import OptionParser, OptionValueError



Mime
View raw message