/* Sends Avro schemas for a table to the client. This is called the first time we send
 * row-level events for a table, as well as every time the schema changes. All subsequent
 * inserts/updates/deletes are assumed to be encoded with this schema. */
int update_frame_with_table_schema(avro_value_t *frame_val, schema_cache_entry *entry) {
    int err = 0;
    avro_value_t msg_val, union_val, record_val, relid_val, key_schema_val,
                 row_schema_val, branch_val;
    bytea *key_schema_json = NULL, *row_schema_json = NULL;

    check(err, avro_value_get_by_index(frame_val, 0, &msg_val, NULL));
    check(err, avro_value_append(&msg_val, &union_val, NULL));
    check(err, avro_value_set_branch(&union_val, PROTOCOL_MSG_TABLE_SCHEMA, &record_val));
    check(err, avro_value_get_by_index(&record_val, 0, &relid_val,      NULL));
    check(err, avro_value_get_by_index(&record_val, 1, &key_schema_val, NULL));
    check(err, avro_value_get_by_index(&record_val, 2, &row_schema_val, NULL));
    check(err, avro_value_set_long(&relid_val, entry->relid));

    if (entry->key_schema) {
        check(err, try_writing(&key_schema_json, &write_schema_json, entry->key_schema));
        check(err, avro_value_set_branch(&key_schema_val, 1, &branch_val));
        check(err, avro_value_set_string_len(&branch_val, VARDATA(key_schema_json),
                    VARSIZE(key_schema_json) - VARHDRSZ + 1));
        pfree(key_schema_json);
    } else {
        check(err, avro_value_set_branch(&key_schema_val, 0, NULL));
    }

    check(err, try_writing(&row_schema_json, &write_schema_json, entry->row_schema));
    check(err, avro_value_set_string_len(&row_schema_val, VARDATA(row_schema_json),
                VARSIZE(row_schema_json) - VARHDRSZ + 1));
    pfree(row_schema_json);
    return err;
}
Пример #2
0
/* Set value. */
void
util::BoolPropertyValue::set_value(bool value)
throw (ReadOnlyProperty)
{
  try_writing();
  m_value = value;
}
/* Updates the given frame with information about a table row that was deleted.
 * This is used only during stream replication. */
int update_frame_with_delete(avro_value_t *frame_val, schema_cache_t cache, Relation rel, HeapTuple oldtuple) {
    int err = 0;
    schema_cache_entry *entry;
    bytea *key_bin = NULL, *old_bin = NULL;

    int changed = schema_cache_lookup(cache, rel, &entry);
    if (changed < 0) {
        return EINVAL;
    } else if (changed) {
        check(err, update_frame_with_table_schema(frame_val, entry));
    }

    if (oldtuple) {
        check(err, extract_tuple_key(entry, rel, RelationGetDescr(rel), oldtuple, &key_bin));
        check(err, avro_value_reset(&entry->row_value));
        check(err, tuple_to_avro_row(&entry->row_value, RelationGetDescr(rel), oldtuple));
        check(err, try_writing(&old_bin, &write_avro_binary, &entry->row_value));
    }

    check(err, update_frame_with_delete_raw(frame_val, RelationGetRelid(rel), key_bin, old_bin));

    if (key_bin) pfree(key_bin);
    if (old_bin) pfree(old_bin);
    return err;
}
/* Updates the given frame with information about a table row that was modified.
 * This is used only during stream replication. */
int update_frame_with_update(avro_value_t *frame_val, schema_cache_t cache, Relation rel, HeapTuple oldtuple, HeapTuple newtuple) {
    int err = 0;
    schema_cache_entry *entry;
    bytea *old_bin = NULL, *new_bin = NULL, *old_key_bin = NULL, *new_key_bin = NULL;

    int changed = schema_cache_lookup(cache, rel, &entry);
    if (changed < 0) {
        return EINVAL;
    } else if (changed) {
        check(err, update_frame_with_table_schema(frame_val, entry));
    }

    /* oldtuple is non-NULL when replident = FULL, or when replident = DEFAULT and there is no
     * primary key, or replident = DEFAULT and the primary key was not modified by the update. */
    if (oldtuple) {
        check(err, extract_tuple_key(entry, rel, RelationGetDescr(rel), oldtuple, &old_key_bin));
        check(err, avro_value_reset(&entry->row_value));
        check(err, tuple_to_avro_row(&entry->row_value, RelationGetDescr(rel), oldtuple));
        check(err, try_writing(&old_bin, &write_avro_binary, &entry->row_value));
    }

    check(err, extract_tuple_key(entry, rel, RelationGetDescr(rel), newtuple, &new_key_bin));
    check(err, avro_value_reset(&entry->row_value));
    check(err, tuple_to_avro_row(&entry->row_value, RelationGetDescr(rel), newtuple));
    check(err, try_writing(&new_bin, &write_avro_binary, &entry->row_value));

    if (old_key_bin != NULL && (VARSIZE(old_key_bin) != VARSIZE(new_key_bin) ||
            memcmp(VARDATA(old_key_bin), VARDATA(new_key_bin), VARSIZE(new_key_bin) - VARHDRSZ) != 0)) {
        /* If the primary key changed, turn the update into a delete and an insert. */
        check(err, update_frame_with_delete_raw(frame_val, RelationGetRelid(rel), old_key_bin, old_bin));
        check(err, update_frame_with_insert_raw(frame_val, RelationGetRelid(rel), new_key_bin, new_bin));
    } else {
        check(err, update_frame_with_update_raw(frame_val, RelationGetRelid(rel), new_key_bin, old_bin, new_bin));
    }

    if (old_key_bin) pfree(old_key_bin);
    if (new_key_bin) pfree(new_key_bin);
    if (old_bin) pfree(old_bin);
    pfree(new_bin);
    return err;
}
Пример #5
0
int write_frame(LogicalDecodingContext *ctx, plugin_state *state) {
    int err = 0;
    bytea *output = NULL;

    check(err, try_writing(&output, &write_avro_binary, &state->frame_value));

    OutputPluginPrepareWrite(ctx, true);
    appendBinaryStringInfo(ctx->out, VARDATA(output), VARSIZE(output) - VARHDRSZ);
    OutputPluginWrite(ctx, true);

    pfree(output);
    return err;
}
/* If we're using a primary key/replica identity index for a given table, this
 * function extracts that index' columns from a row tuple, and encodes the values
 * as an Avro string using the table's key schema. */
int extract_tuple_key(schema_cache_entry *entry, Relation rel, TupleDesc tupdesc, HeapTuple tuple, bytea **key_out) {
    int err = 0;
    Relation index_rel;

    if (entry->key_schema) {
        check(err, avro_value_reset(&entry->key_value));

        index_rel = table_key_index(rel);
        err = tuple_to_avro_key(&entry->key_value, tupdesc, tuple, rel, index_rel->rd_index);
        relation_close(index_rel, AccessShareLock);

        if (err) {
            return err;
        }

        check(err, try_writing(key_out, &write_avro_binary, &entry->key_value));
    }
    return err;
}
/* Updates the given frame value with a tuple inserted into a table. The table
 * schema is automatically included in the frame if it's not in the cache. This
 * function is used both during snapshot and during stream replication.
 *
 * The TupleDesc parameter is not redundant. During stream replication, it is just
 * RelationGetDescr(rel), but during snapshot it is taken from the result set.
 * The difference is that the result set tuple has dropped (logically invisible)
 * columns omitted. */
int update_frame_with_insert(avro_value_t *frame_val, schema_cache_t cache, Relation rel, TupleDesc tupdesc, HeapTuple newtuple) {
    int err = 0;
    schema_cache_entry *entry;
    bytea *key_bin = NULL, *new_bin = NULL;

    int changed = schema_cache_lookup(cache, rel, &entry);
    if (changed) {
        check(err, update_frame_with_table_schema(frame_val, entry));
    }

    check(err, extract_tuple_key(entry, rel, tupdesc, newtuple, &key_bin));
    check(err, avro_value_reset(&entry->row_value));
    check(err, tuple_to_avro_row(&entry->row_value, tupdesc, newtuple));
    check(err, try_writing(&new_bin, &write_avro_binary, &entry->row_value));
    check(err, update_frame_with_insert_raw(frame_val, RelationGetRelid(rel), key_bin, new_bin));

    if (key_bin) pfree(key_bin);
    pfree(new_bin);
    return err;
}