コード例 #1
0
def validate_set_configuration(configuration):
    for table in configuration.tables:
        assert len(
            table.primary_keys
        ) > 0, 'table %s.%s must have associated primary key column(s)' % (
            quote(table.schema),
            quote(table.name),
        )
コード例 #2
0
ファイル: postgresql.py プロジェクト: disqus/pgshovel
def handle_insert(state, operation):
    statement = 'INSERT INTO {schema}.{table} ({columns}) VALUES ({placeholders})'.format(
        schema=quote(operation.schema),
        table=quote(operation.table),
        columns=', '.join(map(quote, map(operator.attrgetter('name'), operation.new.columns))),
        placeholders=', '.join(['%s' for _ in operation.new.columns]),
    )
    parameters = map(operator.itemgetter(1), map(column_converter.to_python, operation.new.columns))
    return statement, parameters
コード例 #3
0
def drop_trigger(cluster, cursor, name, schema, table):
    """
    Drops a log trigger on the provided table for the specified replication set.
    """
    logger.info('Dropping log trigger on %s.%s...', schema, table)
    cursor.execute('DROP TRIGGER {name} ON {schema}.{table}'.format(
        name=quote(cluster.get_trigger_name(name)),
        schema=quote(schema),
        table=quote(table),
    ))
コード例 #4
0
ファイル: __init__.py プロジェクト: disqus/pgshovel
def drop_trigger(cluster, cursor, name, schema, table):
    """
    Drops a log trigger on the provided table for the specified replication set.
    """
    logger.info('Dropping log trigger on %s.%s...', schema, table)
    cursor.execute('DROP TRIGGER {name} ON {schema}.{table}'.format(
        name=quote(cluster.get_trigger_name(name)),
        schema=quote(schema),
        table=quote(table),
    ))
コード例 #5
0
ファイル: postgresql.py プロジェクト: disqus/pgshovel
def handle_delete(state, operation):
    constraints = get_identity_constraints(operation)

    statement = 'DELETE FROM {schema}.{table} WHERE {constraints}'.format(
        schema=quote(operation.schema),
        table=quote(operation.table),
        constraints=', '.join(['%s = %%s' % quote(c[0]) for c in constraints]),
    )
    parameters = [c[1] for c in constraints]
    return statement, parameters
コード例 #6
0
ファイル: postgresql.py プロジェクト: disqus/pgshovel
def handle_update(state, operation):
    constraints = get_identity_constraints(operation)

    statement = 'UPDATE {schema}.{table} SET ({columns}) = ({placeholders}) WHERE {constraints}'.format(
        schema=quote(operation.schema),
        table=quote(operation.table),
        columns=', '.join(map(quote, map(operator.attrgetter('name'), operation.new.columns))),
        placeholders=', '.join(['%s' for _ in operation.new.columns]),
        constraints=', '.join(['%s = %%s' % quote(c[0]) for c in constraints]),
    )
    parameters = map(operator.itemgetter(1), map(column_converter.to_python, operation.new.columns)) + [c[1] for c in constraints]
    return statement, parameters
コード例 #7
0
ファイル: postgresql.py プロジェクト: disqus/pgshovel
 def load(self, table, records):
     # TODO: This should lock and truncate table (check to make sure there
     # are no rows first?), as well as deal with dropping indexes, foreign
     # keys, etc for a more efficient load.
     with self.connection.cursor() as cursor:
         for record in records:
             # TODO: This would be better to move out of the loop, if
             # possible. The column list would need to be exported by the
             # loader itself. (This is probably necessary for loading from
             # files, anyway.)
             statement = 'INSERT INTO {schema}.{table} ({columns}) VALUES ({placeholders})'.format(
                 schema=quote(table.schema),
                 table=quote(table.name),
                 columns=', '.join(map(quote, map(operator.attrgetter('name'), record.columns))),
                 placeholders=', '.join(['%s' for _ in record.columns]),
             )
             cursor.execute(statement, map(operator.itemgetter(1), map(column_converter.to_python, record.columns)))
コード例 #8
0
ファイル: __init__.py プロジェクト: disqus/pgshovel
    def create_trigger(table):
        logger.info('Installing (or replacing) log trigger on %s.%s...', table.schema, table.name)

        primary_keys = unique(list(table.primary_keys))
        all_columns = unique(primary_keys + list(table.columns))
        if table.columns:
            column_list = 'OF %s' % ', '.join(map(quote, all_columns))
        else:
            column_list = ''

        statement = """
            CREATE TRIGGER {name}
            AFTER INSERT OR UPDATE {columns} OR DELETE
            ON {schema}.{table}
            FOR EACH ROW EXECUTE PROCEDURE {cluster_schema}.log(%s, %s, %s, %s)
        """.format(
            name=quote(trigger),
            columns=column_list,
            schema=quote(table.schema),
            table=quote(table.name),
            cluster_schema=quote(cluster.schema),
        )

        cursor.execute("DROP TRIGGER IF EXISTS {name} ON {schema}.{table}".format(
            name=quote(trigger),
            schema=quote(table.schema),
            table=quote(table.name),
        ))

        cursor.execute(statement, (
            cluster.get_queue_name(name),
            pickle.dumps(primary_keys),
            pickle.dumps(all_columns if table.columns else None),
            get_version(configuration)),
        )
コード例 #9
0
ファイル: simple.py プロジェクト: disqus/pgshovel
            def loader(table):
                with connection_lock, connection.cursor('records', cursor_factory=NamedTupleCursor) as cursor:
                    if table.columns:
                        columns = ', '.join(map(quote, table.columns))
                    else:
                        columns = '*'

                    statement = 'SELECT {columns} FROM {schema}.{name}'.format(
                        columns=columns,
                        schema=quote(table.schema),
                        name=quote(table.name),
                    )

                    cursor.execute(statement)
                    for row in cursor:
                        converted = row_converter.to_protobuf(row._asdict())
                        # XXX: This is necessary because of a bug in protocol buffer oneof.
                        yield type(converted).FromString(converted.SerializeToString())
コード例 #10
0
ファイル: database.py プロジェクト: fuziontech/pgshovel
def get_configuration_value(cluster, cursor, key, default=None):
    statement = 'SELECT value FROM {schema}.configuration WHERE key = %s'.format(
        schema=quote(cluster.schema))
    cursor.execute(statement, (key, ))
    results = cursor.fetchall()
    assert len(results) <= 1
    if results:
        return results[0][0]
    else:
        return default
コード例 #11
0
def create_log_trigger_function(cluster, cursor, node_id):
    """
    Installs the log trigger function on the database for the provided cursor,
    returning the function name that can be used as part of a ``CREATE
    TRIGGER`` statement.
    """
    body = resource_string('sql/log_trigger.py.tmpl')
    statement = INSTALL_LOG_TRIGGER_STATEMENT_TEMPLATE.format(
        schema=quote(cluster.schema),
        body=body,
        version=__version__,
    )
    cursor.execute(statement)
コード例 #12
0
ファイル: __init__.py プロジェクト: disqus/pgshovel
def create_log_trigger_function(cluster, cursor, node_id):
    """
    Installs the log trigger function on the database for the provided cursor,
    returning the function name that can be used as part of a ``CREATE
    TRIGGER`` statement.
    """
    body = resource_string('sql/log_trigger.py.tmpl')
    statement = INSTALL_LOG_TRIGGER_STATEMENT_TEMPLATE.format(
        schema=quote(cluster.schema),
        body=body,
        version=__version__,
    )
    cursor.execute(statement)
コード例 #13
0
ファイル: __init__.py プロジェクト: disqus/pgshovel
def setup_database(cluster, cursor):
    """
    Configures a database (the provided cursor) for use with pgshovel.

    This function can also be used to repair a broken installation, or update
    an existing installation's log trigger function.
    """
    # Install PGQ if it doesn't already exist.
    logger.info('Creating PgQ extension (if it does not already exist)...')
    cursor.execute('CREATE EXTENSION IF NOT EXISTS pgq')

    # Install pypythonu if it doesn't already exist.
    logger.info('Creating (or updating) plpythonu language...')
    cursor.execute('CREATE OR REPLACE LANGUAGE plpythonu')

    # Create the schema if it doesn't already exist.
    logger.info('Creating schema (if it does not already exist)...')
    cursor.execute('CREATE SCHEMA IF NOT EXISTS {schema}'.format(
        schema=quote(cluster.schema),
    ))

    # Create the configuration table if it doesn't already exist.
    logger.info('Creating configuration table (if it does not already exist)...')
    create_configuration_table(cluster, cursor)

    # Create the replication state table if it doesn't already exist.
    logger.info('Creating replication state table (if it does not already exist)...')
    create_replication_state_table(cluster, cursor)

    version = get_configuration_value(cluster, cursor, 'version')
    if version is None:
        set_configuration_value(cluster, cursor, 'version', __version__)
    elif version is not None and str(version) != __version__:
        update_configuration_value(cluster, cursor, 'version', __version__)

    # Ensure that this database has already had an identifier associated with it.
    logger.info('Checking for node ID...')
    node_id = get_or_set_node_identifier(cluster, cursor)

    logger.info('Installing (or updating) log trigger function...')
    create_log_trigger_function(cluster, cursor, node_id)

    return node_id
コード例 #14
0
def setup_database(cluster, cursor):
    """
    Configures a database (the provided cursor) for use with pgshovel.

    This function can also be used to repair a broken installation, or update
    an existing installation's log trigger function.
    """
    # Install PGQ if it doesn't already exist.
    logger.info('Creating PgQ extension (if it does not already exist)...')
    cursor.execute('CREATE EXTENSION IF NOT EXISTS pgq')

    # Install pypythonu if it doesn't already exist.
    logger.info('Creating (or updating) plpythonu language...')
    cursor.execute('CREATE OR REPLACE LANGUAGE plpythonu')

    # Create the schema if it doesn't already exist.
    logger.info('Creating schema (if it does not already exist)...')
    cursor.execute('CREATE SCHEMA IF NOT EXISTS {schema}'.format(schema=quote(
        cluster.schema), ))

    # Create the configuration table if it doesn't already exist.
    logger.info(
        'Creating configuration table (if it does not already exist)...')
    create_configuration_table(cluster, cursor)

    version = get_configuration_value(cluster, cursor, 'version')
    if version is None:
        set_configuration_value(cluster, cursor, 'version', __version__)
    elif version is not None and str(version) != __version__:
        update_configuration_value(cluster, cursor, 'version', __version__)

    # Ensure that this database has already had an identifier associated with it.
    logger.info('Checking for node ID...')
    node_id = get_or_set_node_identifier(cluster, cursor)

    logger.info('Installing (or updating) log trigger function...')
    create_log_trigger_function(cluster, cursor, node_id)

    return node_id
コード例 #15
0
    def create_trigger(table):
        logger.info('Installing (or replacing) log trigger on %s.%s...',
                    table.schema, table.name)

        primary_keys = unique(list(table.primary_keys))
        all_columns = unique(primary_keys + list(table.columns))
        if table.columns:
            column_list = 'OF %s' % ', '.join(map(quote, all_columns))
        else:
            column_list = ''

        statement = """
            CREATE TRIGGER {name}
            AFTER INSERT OR UPDATE {columns} OR DELETE
            ON {schema}.{table}
            FOR EACH ROW EXECUTE PROCEDURE {cluster_schema}.log(%s, %s, %s, %s)
        """.format(
            name=quote(trigger),
            columns=column_list,
            schema=quote(table.schema),
            table=quote(table.name),
            cluster_schema=quote(cluster.schema),
        )

        cursor.execute(
            "DROP TRIGGER IF EXISTS {name} ON {schema}.{table}".format(
                name=quote(trigger),
                schema=quote(table.schema),
                table=quote(table.name),
            ))

        cursor.execute(
            statement,
            (cluster.get_queue_name(name), pickle.dumps(primary_keys),
             pickle.dumps(all_columns if table.columns else None),
             get_version(configuration)),
        )
コード例 #16
0
ファイル: database.py プロジェクト: fuziontech/pgshovel
def set_configuration_value(cluster, cursor, key, value):
    statement = 'INSERT INTO {schema}.configuration (key, value) VALUES (%s, %s)'.format(schema=quote(cluster.schema))
    cursor.execute(statement, (key, value))
コード例 #17
0
def create_configuration_table(cluster, cursor):
    statement = INSTALL_CONFIGURATION_TABLE_STATEMENT_TEMPLATE.format(
        schema=quote(cluster.schema), )
    cursor.execute(statement)
コード例 #18
0
ファイル: __init__.py プロジェクト: disqus/pgshovel
def validate_set_configuration(configuration):
    for table in configuration.tables:
        assert len(table.primary_keys) > 0, 'table %s.%s must have associated primary key column(s)' % (quote(table.schema), quote(table.name),)
コード例 #19
0
ファイル: database.py プロジェクト: fuziontech/pgshovel
def update_configuration_value(cluster, cursor, key, value):
    statement = 'UPDATE {schema}.configuration SET value = %s WHERE key = %s'.format(
        schema=quote(cluster.schema))
    cursor.execute(statement, (value, key))
コード例 #20
0
ファイル: database.py プロジェクト: fuziontech/pgshovel
def set_configuration_value(cluster, cursor, key, value):
    statement = 'INSERT INTO {schema}.configuration (key, value) VALUES (%s, %s)'.format(
        schema=quote(cluster.schema))
    cursor.execute(statement, (key, value))
コード例 #21
0
ファイル: database.py プロジェクト: fuziontech/pgshovel
def update_configuration_value(cluster, cursor, key, value):
    statement = 'UPDATE {schema}.configuration SET value = %s WHERE key = %s'.format(schema=quote(cluster.schema))
    cursor.execute(statement, (value, key))
コード例 #22
0
ファイル: __init__.py プロジェクト: disqus/pgshovel
def create_configuration_table(cluster, cursor):
    statement = INSTALL_CONFIGURATION_TABLE_STATEMENT_TEMPLATE.format(
        schema=quote(cluster.schema),
    )
    cursor.execute(statement)
コード例 #23
0
ファイル: database.py プロジェクト: fuziontech/pgshovel
def get_configuration_value(cluster, cursor, key, default=None):
    statement = 'SELECT value FROM {schema}.configuration WHERE key = %s'.format(schema=quote(cluster.schema))
    cursor.execute(statement, (key,))
    results = cursor.fetchall()
    assert len(results) <= 1
    if results:
        return results[0][0]
    else:
        return default
コード例 #24
0
ファイル: __init__.py プロジェクト: disqus/pgshovel
def create_replication_state_table(cluster, cursor):
    statement = INSTALL_REPLICATION_STATE_TABLE_STATEMENT_TEMPLATE.format(
        schema=quote(cluster.schema),
    )
    cursor.execute(statement)