├── .clang-format ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── decoderbufs.control ├── proto └── pg_logicaldec.proto ├── rpms └── postgres-decoderbufs.spec └── src ├── decoderbufs.c └── proto ├── pg_logicaldec.pb-c.c └── pg_logicaldec.pb-c.h /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | # BasedOnStyle: Mozilla 3 | AccessModifierOffset: -2 4 | ConstructorInitializerIndentWidth: 4 5 | AlignEscapedNewlinesLeft: false 6 | AlignTrailingComments: true 7 | AllowAllParametersOfDeclarationOnNextLine: false 8 | AllowShortIfStatementsOnASingleLine: false 9 | AllowShortLoopsOnASingleLine: false 10 | AlwaysBreakTemplateDeclarations: false 11 | AlwaysBreakBeforeMultilineStrings: false 12 | BreakBeforeBinaryOperators: false 13 | BreakBeforeTernaryOperators: true 14 | BreakConstructorInitializersBeforeComma: false 15 | BinPackParameters: true 16 | ColumnLimit: 80 17 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 18 | DerivePointerBinding: true 19 | ExperimentalAutoDetectBinPacking: false 20 | IndentCaseLabels: true 21 | MaxEmptyLinesToKeep: 1 22 | NamespaceIndentation: None 23 | ObjCSpaceBeforeProtocolList: false 24 | PenaltyBreakBeforeFirstCallParameter: 19 25 | PenaltyBreakComment: 60 26 | PenaltyBreakString: 1000 27 | PenaltyBreakFirstLessLess: 120 28 | PenaltyExcessCharacter: 1000000 29 | PenaltyReturnTypeOnItsOwnLine: 200 30 | PointerBindsToType: true 31 | SpacesBeforeTrailingComments: 1 32 | Cpp11BracedListStyle: false 33 | Standard: Cpp03 34 | IndentWidth: 2 35 | TabWidth: 8 36 | UseTab: Never 37 | BreakBeforeBraces: Attach 38 | IndentFunctionDeclarationAfterType: false 39 | SpacesInParentheses: false 40 | SpacesInAngles: false 41 | SpaceInEmptyParentheses: false 42 | SpacesInCStyleCastParentheses: false 43 | SpaceAfterControlStatementKeyword: true 44 | SpaceBeforeAssignmentOperators: true 45 | ContinuationIndentWidth: 4 46 | ... 47 | 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Libraries 8 | *.lib 9 | *.a 10 | 11 | # Shared objects (inc. Windows DLLs) 12 | *.dll 13 | *.so 14 | *.so.* 15 | *.dylib 16 | 17 | # Executables 18 | *.exe 19 | *.out 20 | *.app 21 | *.i*86 22 | *.x86_64 23 | *.hex 24 | 25 | # Eclipse 26 | .cproject 27 | .project 28 | .settings 29 | 30 | # Xcode 31 | *.xcodeproj -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Xavier Stevens 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | MODULE_big = decoderbufs 2 | EXTENSION = decoderbufs 3 | 4 | PROTOBUF_C_CFLAGS = $(shell pkg-config --cflags 'libprotobuf-c >= 1.0.0') 5 | PROTOBUF_C_LDFLAGS = $(shell pkg-config --libs 'libprotobuf-c >= 1.0.0') 6 | 7 | PG_CPPFLAGS += -std=c11 $(PROTOBUF_C_CFLAGS) -I/usr/local/include $(C_PARAMS) 8 | SHLIB_LINK += $(PROTOBUF_C_LDFLAGS) 9 | 10 | OBJS = src/decoderbufs.o src/proto/pg_logicaldec.pb-c.o 11 | 12 | PG_CONFIG ?= pg_config 13 | PGXS := $(shell $(PG_CONFIG) --pgxs) 14 | include $(PGXS) 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![License](https://img.shields.io/badge/license-MIT-brightgreen.svg?maxAge=2592000)](https://opensource.org/licenses/MIT) 2 | [![Version](https://img.shields.io/badge/version-0.1.0-brightgreen.svg?maxAge=2592000)]() 3 | [![User chat](https://img.shields.io/badge/chat-users-brightgreen.svg)](https://gitter.im/debezium/user) 4 | [![Developer chat](https://img.shields.io/badge/chat-devs-brightgreen.svg)](https://gitter.im/debezium/dev) 5 | [![Google Group](https://img.shields.io/:mailing%20list-debezium-brightgreen.svg)](https://groups.google.com/forum/#!forum/debezium) 6 | [![Stack Overflow](http://img.shields.io/:stack%20overflow-debezium-brightgreen.svg)](http://stackoverflow.com/questions/tagged/debezium) 7 | 8 | # Postgres Decoderbufs 9 | 10 | A PostgreSQL logical decoder output plugin to deliver data as [Protocol Buffers](https://developers.google.com/protocol-buffers), adapted for Debezium 11 | 12 | ## Thanks to 13 | 14 | - The original [Decoderbufs Project](https://github.com/xstevens/decoderbufs) on which this is based 15 | - [The PostgreSQL Team](https://postgresql.org) for adding [logical decoding](http://www.postgresql.org/docs/9.4/static/logicaldecoding.html) support 16 | 17 | ## Dependencies 18 | 19 | This code depends on the following libraries and requires them for compilation: 20 | 21 | - [PostgreSQL](http://www.postgresql.org) 9.6+ 22 | - [Protobuf-c](https://github.com/protobuf-c/protobuf-c) 1.2+ - used for data serialization 23 | - [PostGIS](http://www.postgis.net/) 2.1+ - used for Postgres geometric types support 24 | 25 | ## Building 26 | 27 | `postgres-decoderbufs` has to be built from source after installing required dependencies. The required dependencies are first PostgreSQL 28 | (for pg_config), PostgreSQL server development packages, protobuf-c for the Protocol Buffer support and some PostGIS development packages. 29 | 30 | ### Installing Dependencies 31 | 32 | #### Debian 33 | 34 | ```bash 35 | # Core build utilities 36 | apt-get update && apt-get install -f -y software-properties-common build-essential pkg-config git postgresql-server-dev-9.6 37 | 38 | # PostGIS dependency 39 | apt-get install -f -y libproj-dev liblwgeom-dev 40 | 41 | # Protobuf-c dependency (requires a non-stable Debian repo) 42 | add-apt-repository "deb http://ftp.debian.org/debian testing main contrib" && apt-get update 43 | apt-get install -y libprotobuf-c-dev=1.2.1-1+b1 44 | ``` 45 | 46 | When updating the ProtoBuf definition, also install the ProtoBuf C compiler: 47 | 48 | ```bash 49 | apt-get install -y protobuf-c-compiler=1.2.* 50 | ``` 51 | 52 | The above are taken from the Debezium [container images](https://github.com/debezium/docker-images). 53 | 54 | #### Other Linux distributions 55 | 56 | You just need to make sure the above software packages (_or some flavour thereof_) are installed for your distro. 57 | Note that the last step from the above sequence is only required for Debian to be able to install `libprotobuf-c-dev:1.2.1` 58 | 59 | ### Getting the source code 60 | 61 | If you have all of the above prerequisites installed, clone this git repo to build from source: 62 | 63 | ```bash 64 | git clone https://github.com/debezium/postgres-decoderbufs.git 65 | cd postgres-decoderbufs 66 | ``` 67 | 68 | ### Optional: Re-generating ProtoBuf code 69 | 70 | This is only needed after changes to the ProtoBuf definition (_proto/pg_logicaldec.proto): 71 | 72 | ```bash 73 | cd proto 74 | protoc-c --c_out=../src/proto pg_logicaldec.proto 75 | cd .. 76 | ``` 77 | 78 | Commit the generated files to git then. 79 | 80 | ### Building and installing decoderbufs 81 | 82 | If you have multiple Postgres versions installed, you can select which version to install decoderbufs into by altering your `$PATH` to point to the right version. 83 | Then `make` and `make install` for each version. Here is an example: 84 | 85 | ```bash 86 | # Install for Postgres 9.6 if I have multiple local versions 87 | export PATH=/usr/lib/postgresql/9.6/bin:$PATH 88 | make 89 | make install 90 | ``` 91 | 92 | Once the extension has been installed you just need to enable it and logical replication in postgresql.conf: 93 | 94 | ```bash 95 | # MODULES 96 | shared_preload_libraries = 'decoderbufs' 97 | 98 | # REPLICATION 99 | wal_level = logical # minimal, archive, hot_standby, or logical (change requires restart) 100 | max_wal_senders = 8 # max number of walsender processes (change requires restart) 101 | wal_keep_segments = 4 # in logfile segments, 16MB each; 0 disables 102 | #wal_sender_timeout = 60s # in milliseconds; 0 disables 103 | max_replication_slots = 4 # max number of replication slots (change requires restart) 104 | ``` 105 | 106 | In addition, permissions will have to be added for the user that connects to the DB to be able to replicate. This can be modified in _pg\_hba.conf_ like so: 107 | 108 | ```make 109 | local replication trust 110 | host replication 127.0.0.1/32 trust 111 | host replication ::1/128 trust 112 | ``` 113 | 114 | And restart PostgreSQL. 115 | 116 | ## Usage 117 | 118 | ```sql 119 | -- can use SQL for demo purposes 120 | select * from pg_create_logical_replication_slot('decoderbufs_demo', 'decoderbufs'); 121 | 122 | -- DO SOME TABLE MODIFICATIONS (see below about UPDATE/DELETE) 123 | 124 | -- peek at WAL changes using decoderbufs debug mode for SQL console 125 | select data from pg_logical_slot_peek_changes('decoderbufs_demo', NULL, NULL, 'debug-mode', '1'); 126 | -- get WAL changes using decoderbufs to update the WAL position 127 | select data from pg_logical_slot_get_changes('decoderbufs_demo', NULL, NULL, 'debug-mode', '1'); 128 | 129 | -- check the WAL position of logical replicators 130 | select * from pg_replication_slots where slot_type = 'logical'; 131 | ``` 132 | 133 | If you're performing an UPDATE/DELETE on your table and you don't see results for those operations from logical decoding, make sure you have set [REPLICA IDENTITY](http://www.postgresql.org/docs/9.4/static/sql-altertable.html#SQL-CREATETABLE-REPLICA-IDENTITY) appropriately for your use case. 134 | 135 | The binary format will be consumed by the Debezium Postgres Connector. 136 | 137 | ## Type Mappings 138 | 139 | The following table shows how current PostgreSQL type OIDs are mapped to which decoderbuf fields: 140 | 141 | | PostgreSQL Type OID | Decoderbuf Field | 142 | |---------------------|---------------| 143 | | BOOLOID | datum_boolean | 144 | | INT2OID | datum_int32 | 145 | | INT4OID | datum_int32 | 146 | | INT8OID | datum_int64 | 147 | | OIDOID | datum_int64 | 148 | | FLOAT4OID | datum_float | 149 | | FLOAT8OID | datum_double | 150 | | NUMERICOID | datum_double | 151 | | CHAROID | datum_string | 152 | | VARCHAROID | datum_string | 153 | | BPCHAROID | datum_string | 154 | | TEXTOID | datum_string | 155 | | JSONOID | datum_string | 156 | | XMLOID | datum_string | 157 | | UUIDOID | datum_string | 158 | | TIMESTAMPOID | datum_string | 159 | | TIMESTAMPTZOID | datum_string | 160 | | BYTEAOID | datum_bytes | 161 | | POINTOID | datum_point | 162 | | PostGIS geometry | datum_point | 163 | | PostGIS geography | datum_point | 164 | 165 | ## Support 166 | 167 | File bug reports and feature requests using [Debezium's JIRA](https://issues.jboss.org/browse/DBZ) and the 168 | [postgresql-connector](https://issues.jboss.org/browse/DBZ/component/12323543) component 169 | -------------------------------------------------------------------------------- /decoderbufs.control: -------------------------------------------------------------------------------- 1 | comment = 'Logical decoding plugin that delivers WAL stream changes using a Protocol Buffer format' 2 | default_version = '0.1.0' 3 | relocatable = true 4 | -------------------------------------------------------------------------------- /proto/pg_logicaldec.proto: -------------------------------------------------------------------------------- 1 | package decoderbufs; 2 | 3 | option java_package="io.debezium.connector.postgresql.proto"; 4 | option java_outer_classname = "PgProto"; 5 | option optimize_for = SPEED; 6 | 7 | enum Op { 8 | UNKNOWN = -1; 9 | INSERT = 0; 10 | UPDATE = 1; 11 | DELETE = 2; 12 | BEGIN = 3; 13 | COMMIT = 4; 14 | } 15 | 16 | message Point { 17 | required double x = 1; 18 | required double y = 2; 19 | } 20 | 21 | message DatumMessage { 22 | optional string column_name = 1; 23 | optional int64 column_type = 2; 24 | oneof datum { 25 | int32 datum_int32 = 3; 26 | int64 datum_int64 = 4; 27 | float datum_float = 5; 28 | double datum_double = 6; 29 | bool datum_bool = 7; 30 | string datum_string = 8; 31 | bytes datum_bytes = 9; 32 | Point datum_point = 10; 33 | bool datum_missing = 11; 34 | } 35 | } 36 | 37 | message TypeInfo { 38 | required string modifier = 1; 39 | required bool value_optional = 2; 40 | } 41 | 42 | message RowMessage { 43 | optional uint32 transaction_id = 1; 44 | optional uint64 commit_time = 2; 45 | optional string table = 3; 46 | optional Op op = 4; 47 | repeated DatumMessage new_tuple = 5; 48 | repeated DatumMessage old_tuple = 6; 49 | repeated TypeInfo new_typeinfo = 7; 50 | } 51 | -------------------------------------------------------------------------------- /rpms/postgres-decoderbufs.spec: -------------------------------------------------------------------------------- 1 | Name: postgres-decoderbufs 2 | Version: 0.10.0 3 | Release: 1%{?dist} 4 | Summary: PostgreSQL Protocol Buffers logical decoder plugin 5 | 6 | License: MIT 7 | URL: https://github.com/debezium/postgres-decoderbufs 8 | 9 | %global full_version %{version}.Final 10 | 11 | Source0: https://github.com/debezium/%{name}/archive/v%{full_version}.tar.gz 12 | 13 | BuildRequires: gcc 14 | BuildRequires: postgresql-devel >= 9.6, postgresql-server-devel >= 9.6 15 | BuildRequires: postgis-devel >= 2 16 | BuildRequires: protobuf-c-devel 17 | 18 | Requires: protobuf-c 19 | %{?postgresql_module_requires} 20 | 21 | Recommends: postgis 22 | 23 | %description 24 | A PostgreSQL logical decoder output plugin to deliver data as Protocol Buffers messages. 25 | 26 | %prep 27 | %setup -qn postgres-decoderbufs-%{full_version} 28 | 29 | 30 | %build 31 | %make_build 32 | 33 | 34 | %install 35 | %make_install 36 | 37 | 38 | %files 39 | %doc README.md 40 | %license LICENSE 41 | %{_libdir}/pgsql/decoderbufs.so 42 | %{_datadir}/pgsql/extension/decoderbufs.control 43 | 44 | 45 | %changelog 46 | * Wed Oct 9 2019 - Jiri Pechanec 0.10.0-1 47 | * Tue May 14 2019 - Jiri Pechanec 0.9.5-1 48 | - Initial RPM packaging 49 | -------------------------------------------------------------------------------- /src/decoderbufs.c: -------------------------------------------------------------------------------- 1 | /* 2 | * decoderbufs - PostgreSQL output plug-in for logical replication to Protocol 3 | * Buffers 4 | * 5 | * Copyright (c) 2014 Xavier Stevens 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | #if defined(__linux__) 27 | #include 28 | #elif defined(__APPLE__) 29 | #include 30 | #include 31 | #define htobe64(x) OSSwapHostToBigInt64(x) 32 | #endif 33 | #include 34 | 35 | #include "postgres.h" 36 | #include "funcapi.h" 37 | #include "catalog/pg_class.h" 38 | #include "catalog/pg_type.h" 39 | #include "catalog/namespace.h" 40 | #include "executor/spi.h" 41 | #include "replication/output_plugin.h" 42 | #include "replication/logical.h" 43 | #include "utils/builtins.h" 44 | #include "utils/lsyscache.h" 45 | #include "utils/geo_decls.h" 46 | #include "utils/json.h" 47 | #include "utils/memutils.h" 48 | #include "utils/numeric.h" 49 | #include "utils/rel.h" 50 | #include "utils/relcache.h" 51 | #include "utils/syscache.h" 52 | #include "utils/typcache.h" 53 | #include "utils/uuid.h" 54 | #include "utils/timestamp.h" 55 | #include "utils/date.h" 56 | #include "utils/cash.h" 57 | #include "proto/pg_logicaldec.pb-c.h" 58 | 59 | #ifndef HAVE_INT64_TIMESTAMP 60 | #error Expecting timestamps to be represented as integers, not as floating-point. 61 | #endif 62 | 63 | #if PG_VERSION_NUM >= 170000 64 | #define TUPLE_ACCESS(x) x 65 | #else 66 | #define TUPLE_ACCESS(x) &x->tuple 67 | #endif 68 | 69 | PG_MODULE_MAGIC; 70 | 71 | /* define a time macro to convert TimestampTz into something more sane, 72 | * which in this case is microseconds since epoch. This is because PG stores internally the timestamps relative to 73 | * 2000-01-01T00:00:00Z and not the Unix epoch. 74 | */ 75 | #define TIMESTAMPTZ_TO_USEC_SINCE_EPOCH(t) t + (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * USECS_PER_DAY; 76 | #define DATE_TO_DAYS_SINCE_EPOCH(t) t + (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE); 77 | 78 | typedef struct { 79 | MemoryContext context; 80 | bool debug_mode; 81 | } DecoderData; 82 | 83 | /* these must be available to pg_dlsym() */ 84 | extern void _PG_init(void); 85 | extern void _PG_output_plugin_init(OutputPluginCallbacks *cb); 86 | 87 | static void pg_decode_startup(LogicalDecodingContext *ctx, 88 | OutputPluginOptions *opt, bool is_init); 89 | static void pg_decode_shutdown(LogicalDecodingContext *ctx); 90 | static void pg_decode_begin_txn(LogicalDecodingContext *ctx, 91 | ReorderBufferTXN *txn); 92 | static void pg_decode_commit_txn(LogicalDecodingContext *ctx, 93 | ReorderBufferTXN *txn, XLogRecPtr commit_lsn); 94 | static void pg_decode_change(LogicalDecodingContext *ctx, ReorderBufferTXN *txn, 95 | Relation rel, ReorderBufferChange *change); 96 | 97 | void _PG_init(void) {} 98 | 99 | /* specify output plugin callbacks */ 100 | void _PG_output_plugin_init(OutputPluginCallbacks *cb) { 101 | AssertVariableIsOfType(&_PG_output_plugin_init, LogicalOutputPluginInit); 102 | cb->startup_cb = pg_decode_startup; 103 | cb->begin_cb = pg_decode_begin_txn; 104 | cb->change_cb = pg_decode_change; 105 | cb->commit_cb = pg_decode_commit_txn; 106 | cb->shutdown_cb = pg_decode_shutdown; 107 | } 108 | 109 | /* initialize this plugin */ 110 | static void pg_decode_startup(LogicalDecodingContext *ctx, 111 | OutputPluginOptions *opt, bool is_init) { 112 | ListCell *option; 113 | DecoderData *data; 114 | 115 | elog(DEBUG1, "Entering startup callback"); 116 | 117 | data = palloc(sizeof(DecoderData)); 118 | #if PG_VERSION_NUM >= 90600 119 | data->context = AllocSetContextCreate( 120 | ctx->context, "decoderbufs context", ALLOCSET_DEFAULT_SIZES); 121 | #else 122 | data->context = AllocSetContextCreate( 123 | ctx->context, "decoderbufs context", ALLOCSET_DEFAULT_MINSIZE, 124 | ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); 125 | #endif 126 | data->debug_mode = false; 127 | opt->output_type = OUTPUT_PLUGIN_BINARY_OUTPUT; 128 | 129 | foreach(option, ctx->output_plugin_options) { 130 | DefElem *elem = lfirst(option); 131 | Assert(elem->arg == NULL || IsA(elem->arg, String)); 132 | 133 | if (strcmp(elem->defname, "debug-mode") == 0) { 134 | if (elem->arg == NULL) { 135 | data->debug_mode = false; 136 | } else if (!parse_bool(strVal(elem->arg), &data->debug_mode)) { 137 | ereport(ERROR, 138 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), 139 | errmsg("could not parse value \"%s\" for parameter \"%s\"", 140 | strVal(elem->arg), elem->defname))); 141 | } 142 | 143 | if (data->debug_mode) { 144 | elog(NOTICE, "Decoderbufs DEBUG MODE is ON."); 145 | opt->output_type = OUTPUT_PLUGIN_TEXTUAL_OUTPUT; 146 | } else { 147 | elog(NOTICE, "Decoderbufs DEBUG MODE is OFF."); 148 | } 149 | } else { 150 | ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), 151 | errmsg("option \"%s\" = \"%s\" is unknown", elem->defname, 152 | elem->arg ? strVal(elem->arg) : "(null)"))); 153 | } 154 | } 155 | 156 | ctx->output_plugin_private = data; 157 | 158 | elog(INFO, "Exiting startup callback"); 159 | } 160 | 161 | /* cleanup this plugin's resources */ 162 | static void pg_decode_shutdown(LogicalDecodingContext *ctx) { 163 | DecoderData *data; 164 | 165 | elog(DEBUG1, "Entering decode_shutdown callback"); 166 | 167 | data = ctx->output_plugin_private; 168 | 169 | /* cleanup our own resources via memory context reset */ 170 | MemoryContextDelete(data->context); 171 | } 172 | 173 | /* print tuple datums (only used for debug-mode) */ 174 | static void print_tuple_datums(StringInfo out, Decoderbufs__DatumMessage **tup, 175 | size_t n) { 176 | if (tup) { 177 | for (int i = 0; i < n; i++) { 178 | Decoderbufs__DatumMessage *dmsg = tup[i]; 179 | 180 | if (dmsg->column_name) 181 | appendStringInfo(out, "column_name[%s]", dmsg->column_name); 182 | 183 | if (dmsg->has_column_type) 184 | appendStringInfo(out, ", column_type[%" PRId64 "]", dmsg->column_type); 185 | 186 | switch (dmsg->datum_case) { 187 | case DECODERBUFS__DATUM_MESSAGE__DATUM_DATUM_INT32: 188 | appendStringInfo(out, ", datum[%d]", dmsg->datum_int32); 189 | break; 190 | case DECODERBUFS__DATUM_MESSAGE__DATUM_DATUM_INT64: 191 | appendStringInfo(out, ", datum[%" PRId64 "]", dmsg->datum_int64); 192 | break; 193 | case DECODERBUFS__DATUM_MESSAGE__DATUM_DATUM_FLOAT: 194 | appendStringInfo(out, ", datum[%f]", dmsg->datum_float); 195 | break; 196 | case DECODERBUFS__DATUM_MESSAGE__DATUM_DATUM_DOUBLE: 197 | appendStringInfo(out, ", datum[%f]", dmsg->datum_double); 198 | break; 199 | case DECODERBUFS__DATUM_MESSAGE__DATUM_DATUM_BOOL: 200 | appendStringInfo(out, ", datum[%d]", dmsg->datum_bool); 201 | break; 202 | case DECODERBUFS__DATUM_MESSAGE__DATUM_DATUM_STRING: 203 | appendStringInfo(out, ", datum[%s]", dmsg->datum_string); 204 | break; 205 | case DECODERBUFS__DATUM_MESSAGE__DATUM_DATUM_BYTES: 206 | break; 207 | case DECODERBUFS__DATUM_MESSAGE__DATUM_DATUM_POINT: 208 | appendStringInfo(out, ", datum[POINT(%f, %f)]", 209 | dmsg->datum_point->x, dmsg->datum_point->y); 210 | break; 211 | case DECODERBUFS__DATUM_MESSAGE__DATUM__NOT_SET: 212 | // intentional fall-through 213 | default: 214 | appendStringInfo(out, ", datum[!NOT SET!]"); 215 | break; 216 | } 217 | appendStringInfo(out, "\n"); 218 | } 219 | } 220 | } 221 | 222 | /* print a row message (only used for debug-mode) */ 223 | static void print_row_msg(StringInfo out, Decoderbufs__RowMessage *rmsg) { 224 | if (!rmsg) 225 | return; 226 | 227 | if (rmsg->has_transaction_id) 228 | appendStringInfo(out, "txid[%d]", rmsg->transaction_id); 229 | 230 | if (rmsg->has_commit_time) 231 | appendStringInfo(out, ", commit_time[%" PRId64 "]", rmsg->commit_time); 232 | 233 | if (rmsg->table) 234 | appendStringInfo(out, ", table[%s]", rmsg->table); 235 | 236 | if (rmsg->has_op) 237 | appendStringInfo(out, ", op[%d]", rmsg->op); 238 | 239 | if (rmsg->old_tuple) { 240 | appendStringInfo(out, "\nOLD TUPLE: \n"); 241 | print_tuple_datums(out, rmsg->old_tuple, rmsg->n_old_tuple); 242 | appendStringInfo(out, "\n"); 243 | } 244 | 245 | if (rmsg->new_tuple) { 246 | appendStringInfo(out, "\nNEW TUPLE: \n"); 247 | print_tuple_datums(out, rmsg->new_tuple, rmsg->n_new_tuple); 248 | appendStringInfo(out, "\n"); 249 | } 250 | 251 | } 252 | 253 | /* set a datum value based on its OID specified by typid */ 254 | static void set_datum_value(Decoderbufs__DatumMessage *datum_msg, Oid typid, 255 | Oid typoutput, Datum datum) { 256 | bytea *valptr = NULL; 257 | const char *output = NULL; 258 | Point *p = NULL; 259 | Timestamp ts; 260 | TimeTzADT *timetz = NULL; 261 | Decoderbufs__Point dp = DECODERBUFS__POINT__INIT; 262 | 263 | int size = 0; 264 | switch (typid) { 265 | case BOOLOID: 266 | datum_msg->datum_bool = DatumGetBool(datum); 267 | datum_msg->datum_case = DECODERBUFS__DATUM_MESSAGE__DATUM_DATUM_BOOL; 268 | break; 269 | case INT2OID: 270 | datum_msg->datum_int32 = DatumGetInt16(datum); 271 | datum_msg->datum_case = DECODERBUFS__DATUM_MESSAGE__DATUM_DATUM_INT32; 272 | break; 273 | case INT4OID: 274 | datum_msg->datum_int32 = DatumGetInt32(datum); 275 | datum_msg->datum_case = DECODERBUFS__DATUM_MESSAGE__DATUM_DATUM_INT32; 276 | break; 277 | case INT8OID: 278 | datum_msg->datum_int64 = DatumGetInt64(datum); 279 | datum_msg->datum_case = DECODERBUFS__DATUM_MESSAGE__DATUM_DATUM_INT64; 280 | break; 281 | case OIDOID: 282 | datum_msg->datum_int64 = (Oid) DatumGetUInt64(datum); 283 | datum_msg->datum_case = DECODERBUFS__DATUM_MESSAGE__DATUM_DATUM_INT64; 284 | break; 285 | case FLOAT4OID: 286 | datum_msg->datum_float = DatumGetFloat4(datum); 287 | datum_msg->datum_case = DECODERBUFS__DATUM_MESSAGE__DATUM_DATUM_FLOAT; 288 | break; 289 | case FLOAT8OID: 290 | datum_msg->datum_double = DatumGetFloat8(datum); 291 | datum_msg->datum_case = DECODERBUFS__DATUM_MESSAGE__DATUM_DATUM_DOUBLE; 292 | break; 293 | case CASHOID: 294 | datum_msg->datum_int64 = DatumGetCash(datum); 295 | datum_msg->datum_case = DECODERBUFS__DATUM_MESSAGE__DATUM_DATUM_INT64; 296 | break; 297 | case NUMERICOID: 298 | case CHAROID: 299 | case VARCHAROID: 300 | case BPCHAROID: 301 | case TEXTOID: 302 | case JSONOID: 303 | case JSONBOID: 304 | case XMLOID: 305 | case BITOID: 306 | case VARBITOID: 307 | case UUIDOID: 308 | case INTERVALOID: 309 | output = OidOutputFunctionCall(typoutput, datum); 310 | datum_msg->datum_string = pnstrdup(output, strlen(output)); 311 | datum_msg->datum_case = DECODERBUFS__DATUM_MESSAGE__DATUM_DATUM_STRING; 312 | break; 313 | case TIMESTAMPOID: 314 | ts = DatumGetTimestamp(datum); 315 | if (TIMESTAMP_NOT_FINITE(ts)) { 316 | datum_msg->datum_int64 = ts; 317 | } else { 318 | datum_msg->datum_int64 = TIMESTAMPTZ_TO_USEC_SINCE_EPOCH(ts); 319 | } 320 | datum_msg->datum_case = DECODERBUFS__DATUM_MESSAGE__DATUM_DATUM_INT64; 321 | break; 322 | case TIMESTAMPTZOID: 323 | ts = DatumGetTimestampTz(datum); 324 | if (TIMESTAMP_NOT_FINITE(ts)) { 325 | datum_msg->datum_int64 = ts; 326 | } else { 327 | datum_msg->datum_int64 = TIMESTAMPTZ_TO_USEC_SINCE_EPOCH(ts); 328 | } 329 | datum_msg->datum_case = DECODERBUFS__DATUM_MESSAGE__DATUM_DATUM_INT64; 330 | break; 331 | case DATEOID: 332 | /* simply get the number of days as the stored 32 bit value and convert to EPOCH */ 333 | datum_msg->datum_int32 = DATE_TO_DAYS_SINCE_EPOCH(DatumGetDateADT(datum)); 334 | datum_msg->datum_case = DECODERBUFS__DATUM_MESSAGE__DATUM_DATUM_INT32; 335 | break; 336 | case TIMEOID: 337 | datum_msg->datum_int64 = DatumGetTimeADT(datum); 338 | datum_msg->datum_case = DECODERBUFS__DATUM_MESSAGE__DATUM_DATUM_INT64; 339 | break; 340 | case TIMETZOID: 341 | timetz = DatumGetTimeTzADTP(datum); 342 | /* use GMT-equivalent time */ 343 | datum_msg->datum_double = (double) (timetz->time + (timetz->zone * 1000000.0)); 344 | datum_msg->datum_case = DECODERBUFS__DATUM_MESSAGE__DATUM_DATUM_DOUBLE; 345 | break; 346 | case BYTEAOID: 347 | valptr = DatumGetByteaPCopy(datum); 348 | size = VARSIZE(valptr) - VARHDRSZ; 349 | datum_msg->datum_bytes.data = palloc(size); 350 | memcpy(datum_msg->datum_bytes.data, (uint8_t *)VARDATA(valptr), size); 351 | datum_msg->datum_bytes.len = size; 352 | datum_msg->datum_case = DECODERBUFS__DATUM_MESSAGE__DATUM_DATUM_BYTES; 353 | break; 354 | case POINTOID: 355 | p = DatumGetPointP(datum); 356 | dp.x = p->x; 357 | dp.y = p->y; 358 | datum_msg->datum_point = palloc(sizeof(Decoderbufs__Point)); 359 | memcpy(datum_msg->datum_point, &dp, sizeof(dp)); 360 | datum_msg->datum_case = DECODERBUFS__DATUM_MESSAGE__DATUM_DATUM_POINT; 361 | break; 362 | default: 363 | { 364 | int len; 365 | elog(DEBUG1, "Encountered unknown typid: %d, using bytes", typid); 366 | output = OidOutputFunctionCall(typoutput, datum); 367 | len = strlen(output); 368 | size = sizeof(char) * len; 369 | datum_msg->datum_bytes.data = palloc(size); 370 | memcpy(datum_msg->datum_bytes.data, (uint8_t *)output, size); 371 | datum_msg->datum_bytes.len = len; 372 | datum_msg->datum_case = DECODERBUFS__DATUM_MESSAGE__DATUM_DATUM_BYTES; 373 | } 374 | break; 375 | } 376 | } 377 | 378 | /* return the number of valid attributes from the tuple */ 379 | static int valid_attributes_count_from(TupleDesc tupdesc) { 380 | int natt; 381 | int count = 0; 382 | for (natt = 0; natt < tupdesc->natts; natt++) { 383 | Form_pg_attribute attr = TupleDescAttr(tupdesc, natt); 384 | 385 | /* skip dropped columns and system columns */ 386 | if (attr->attisdropped || attr->attnum < 0) { 387 | continue; 388 | } 389 | count++; 390 | } 391 | return count; 392 | } 393 | 394 | /* convert a PG tuple to an array of DatumMessage(s) */ 395 | static void tuple_to_tuple_msg(Decoderbufs__DatumMessage **tmsg, 396 | Relation relation, HeapTuple tuple, 397 | TupleDesc tupdesc) { 398 | int natt; 399 | int valid_attr_cnt = 0; 400 | elog(DEBUG1, "processing tuple with %d columns", tupdesc->natts); 401 | /* build column names and values */ 402 | for (natt = 0; natt < tupdesc->natts; natt++) { 403 | Form_pg_attribute attr; 404 | Datum origval; 405 | bool isnull; 406 | const char *attrName; 407 | Oid typoutput; 408 | bool typisvarlena; 409 | Decoderbufs__DatumMessage datum_msg = DECODERBUFS__DATUM_MESSAGE__INIT; 410 | 411 | attr = TupleDescAttr(tupdesc, natt); 412 | 413 | /* skip dropped columns and system columns */ 414 | if (attr->attisdropped || attr->attnum < 0) { 415 | elog(DEBUG1, "skipping column %d because %s", natt + 1, attr->attisdropped ? "it's a dropped column" : "it's a system column"); 416 | continue; 417 | } 418 | 419 | attrName = quote_identifier(NameStr(attr->attname)); 420 | elog(DEBUG1, "processing column %d with name %s", natt + 1, attrName); 421 | 422 | /* set the column name */ 423 | datum_msg.column_name = (char *)attrName; 424 | 425 | /* set datum from tuple */ 426 | origval = heap_getattr(tuple, natt + 1, tupdesc, &isnull); 427 | 428 | /* get output function */ 429 | datum_msg.column_type = attr->atttypid; 430 | datum_msg.has_column_type = true; 431 | 432 | /* query output function */ 433 | getTypeOutputInfo(attr->atttypid, &typoutput, &typisvarlena); 434 | if (!isnull) { 435 | if (typisvarlena && VARATT_IS_EXTERNAL_ONDISK(origval)) { 436 | datum_msg.datum_missing = true; 437 | datum_msg.datum_case = DECODERBUFS__DATUM_MESSAGE__DATUM_DATUM_MISSING; 438 | elog(DEBUG1, "Not handling external on disk varlena at the moment."); 439 | } else if (!typisvarlena) { 440 | set_datum_value(&datum_msg, attr->atttypid, typoutput, origval); 441 | } else { 442 | Datum val = PointerGetDatum(PG_DETOAST_DATUM(origval)); 443 | set_datum_value(&datum_msg, attr->atttypid, typoutput, val); 444 | } 445 | } else { 446 | elog(DEBUG1, "column %s is null, ignoring value", attrName); 447 | } 448 | 449 | tmsg[valid_attr_cnt] = palloc(sizeof(datum_msg)); 450 | memcpy(tmsg[valid_attr_cnt], &datum_msg, sizeof(datum_msg)); 451 | 452 | valid_attr_cnt++; 453 | } 454 | } 455 | 456 | /* provide a metadata for new tuple */ 457 | static void add_metadata_to_msg(Decoderbufs__TypeInfo **tmsg, 458 | Relation relation, HeapTuple tuple, 459 | TupleDesc tupdesc) { 460 | int natt; 461 | int valid_attr_cnt = 0; 462 | elog(DEBUG1, "Adding metadata for %d columns", tupdesc->natts); 463 | /* build column names and values */ 464 | for (natt = 0; natt < tupdesc->natts; natt++) { 465 | Form_pg_attribute attr; 466 | char *typ_mod; 467 | Decoderbufs__TypeInfo typeinfo = DECODERBUFS__TYPE_INFO__INIT; 468 | bool not_null; 469 | 470 | attr = TupleDescAttr(tupdesc, natt); 471 | 472 | /* skip dropped columns and system columns */ 473 | if (attr->attisdropped || attr->attnum < 0) { 474 | elog(DEBUG1, "skipping column %d because %s", natt + 1, attr->attisdropped ? "it's a dropped column" : "it's a system column"); 475 | continue; 476 | } 477 | 478 | not_null = attr->attnotnull; 479 | typ_mod = TextDatumGetCString(DirectFunctionCall2(format_type, attr->atttypid, attr->atttypmod)); 480 | elog(DEBUG1, "Adding typemodifier '%s' for column %d, optional %s", typ_mod, natt, !not_null ? "true" : "false"); 481 | 482 | typeinfo.modifier = typ_mod; 483 | typeinfo.value_optional = !not_null; 484 | tmsg[valid_attr_cnt] = palloc(sizeof(typeinfo)); 485 | memcpy(tmsg[valid_attr_cnt], &typeinfo, sizeof(typeinfo)); 486 | 487 | valid_attr_cnt++; 488 | } 489 | } 490 | 491 | /* BEGIN callback */ 492 | static void pg_decode_begin_txn(LogicalDecodingContext *ctx, 493 | ReorderBufferTXN *txn) { 494 | 495 | DecoderData *data; 496 | MemoryContext old; 497 | Decoderbufs__RowMessage rmsg = DECODERBUFS__ROW_MESSAGE__INIT; 498 | elog(DEBUG1, "Entering begin callback"); 499 | 500 | 501 | /* Avoid leaking memory by using and resetting our own context */ 502 | data = ctx->output_plugin_private; 503 | old = MemoryContextSwitchTo(data->context); 504 | 505 | rmsg.op = DECODERBUFS__OP__BEGIN; 506 | rmsg.has_op = true; 507 | rmsg.transaction_id = txn->xid; 508 | rmsg.has_transaction_id = true; 509 | #if PG_VERSION_NUM >= 150000 510 | rmsg.commit_time = TIMESTAMPTZ_TO_USEC_SINCE_EPOCH(txn->xact_time.commit_time); 511 | #else 512 | rmsg.commit_time = TIMESTAMPTZ_TO_USEC_SINCE_EPOCH(txn->commit_time); 513 | #endif 514 | rmsg.has_commit_time = true; 515 | 516 | /* write msg */ 517 | OutputPluginPrepareWrite(ctx, true); 518 | if (data->debug_mode) { 519 | print_row_msg(ctx->out, &rmsg); 520 | } else { 521 | size_t psize = decoderbufs__row_message__get_packed_size(&rmsg); 522 | void *packed = palloc(psize); 523 | size_t ssize = decoderbufs__row_message__pack(&rmsg, packed); 524 | appendBinaryStringInfo(ctx->out, packed, ssize); 525 | } 526 | OutputPluginWrite(ctx, true); 527 | 528 | /* Cleanup, freeing memory */ 529 | MemoryContextSwitchTo(old); 530 | MemoryContextReset(data->context); 531 | } 532 | 533 | /* COMMIT callback */ 534 | static void pg_decode_commit_txn(LogicalDecodingContext *ctx, 535 | ReorderBufferTXN *txn, XLogRecPtr commit_lsn) { 536 | 537 | DecoderData *data; 538 | MemoryContext old; 539 | Decoderbufs__RowMessage rmsg = DECODERBUFS__ROW_MESSAGE__INIT; 540 | elog(DEBUG1, "Entering commit callback"); 541 | 542 | 543 | /* Avoid leaking memory by using and resetting our own context */ 544 | data = ctx->output_plugin_private; 545 | old = MemoryContextSwitchTo(data->context); 546 | 547 | rmsg.op = DECODERBUFS__OP__COMMIT; 548 | rmsg.has_op = true; 549 | rmsg.transaction_id = txn->xid; 550 | rmsg.has_transaction_id = true; 551 | #if PG_VERSION_NUM >= 150000 552 | rmsg.commit_time = TIMESTAMPTZ_TO_USEC_SINCE_EPOCH(txn->xact_time.commit_time); 553 | #else 554 | rmsg.commit_time = TIMESTAMPTZ_TO_USEC_SINCE_EPOCH(txn->commit_time); 555 | #endif 556 | rmsg.has_commit_time = true; 557 | 558 | /* write msg */ 559 | OutputPluginPrepareWrite(ctx, true); 560 | if (data->debug_mode) { 561 | print_row_msg(ctx->out, &rmsg); 562 | } else { 563 | size_t psize = decoderbufs__row_message__get_packed_size(&rmsg); 564 | void *packed = palloc(psize); 565 | size_t ssize = decoderbufs__row_message__pack(&rmsg, packed); 566 | appendBinaryStringInfo(ctx->out, packed, ssize); 567 | } 568 | OutputPluginWrite(ctx, true); 569 | 570 | /* Cleanup, freeing memory */ 571 | MemoryContextSwitchTo(old); 572 | MemoryContextReset(data->context); 573 | } 574 | 575 | /* callback for individual changed tuples */ 576 | static void pg_decode_change(LogicalDecodingContext *ctx, ReorderBufferTXN *txn, 577 | Relation relation, ReorderBufferChange *change) { 578 | DecoderData *data; 579 | MemoryContext old; 580 | 581 | Form_pg_class class_form; 582 | char replident; 583 | bool is_rel_non_selective; 584 | const char *selectiveInfo; 585 | TupleDesc tupdesc; 586 | Decoderbufs__RowMessage rmsg = DECODERBUFS__ROW_MESSAGE__INIT; 587 | 588 | elog(DEBUG1, "Entering decode_change callback"); 589 | 590 | /* Avoid leaking memory by using and resetting our own context */ 591 | data = ctx->output_plugin_private; 592 | old = MemoryContextSwitchTo(data->context); 593 | 594 | replident = relation->rd_rel->relreplident; 595 | 596 | class_form = RelationGetForm(relation); 597 | 598 | RelationGetIndexList(relation); 599 | is_rel_non_selective = (replident == REPLICA_IDENTITY_NOTHING || 600 | (replident == REPLICA_IDENTITY_DEFAULT && 601 | !OidIsValid(relation->rd_replidindex))); 602 | selectiveInfo = is_rel_non_selective ? "non selective" : "selective"; 603 | 604 | /* set common fields */ 605 | rmsg.transaction_id = txn->xid; 606 | rmsg.has_transaction_id = true; 607 | #if PG_VERSION_NUM >= 150000 608 | rmsg.commit_time = TIMESTAMPTZ_TO_USEC_SINCE_EPOCH(txn->xact_time.commit_time); 609 | #else 610 | rmsg.commit_time = TIMESTAMPTZ_TO_USEC_SINCE_EPOCH(txn->commit_time); 611 | #endif 612 | rmsg.has_commit_time = true; 613 | rmsg.table = pstrdup(quote_qualified_identifier(get_namespace_name(get_rel_namespace(RelationGetRelid(relation))), 614 | NameStr(class_form->relname))); 615 | 616 | 617 | /* decode different operation types */ 618 | switch (change->action) { 619 | case REORDER_BUFFER_CHANGE_INSERT: 620 | elog(DEBUG1, "decoding INSERT for table %s; relation is %s", rmsg.table, selectiveInfo); 621 | rmsg.op = DECODERBUFS__OP__INSERT; 622 | rmsg.has_op = true; 623 | if (change->data.tp.newtuple != NULL) { 624 | elog(DEBUG1, "decoding new tuple information"); 625 | tupdesc = RelationGetDescr(relation); 626 | 627 | rmsg.n_new_tuple = valid_attributes_count_from(tupdesc); 628 | rmsg.new_tuple = 629 | palloc(sizeof(Decoderbufs__DatumMessage*) * rmsg.n_new_tuple); 630 | tuple_to_tuple_msg(rmsg.new_tuple, relation, 631 | TUPLE_ACCESS(change->data.tp.newtuple), tupdesc); 632 | 633 | rmsg.n_new_typeinfo = rmsg.n_new_tuple; 634 | rmsg.new_typeinfo = 635 | palloc(sizeof(Decoderbufs__TypeInfo*) * rmsg.n_new_typeinfo); 636 | add_metadata_to_msg(rmsg.new_typeinfo, relation, 637 | TUPLE_ACCESS(change->data.tp.newtuple), tupdesc); 638 | } 639 | break; 640 | case REORDER_BUFFER_CHANGE_UPDATE: 641 | rmsg.op = DECODERBUFS__OP__UPDATE; 642 | rmsg.has_op = true; 643 | elog(DEBUG1, "decoding UPDATE for table %s; relation is %s", rmsg.table, selectiveInfo); 644 | if (!is_rel_non_selective) { 645 | if (change->data.tp.oldtuple != NULL) { 646 | elog(DEBUG1, "decoding old tuple information"); 647 | tupdesc = RelationGetDescr(relation); 648 | rmsg.n_old_tuple = valid_attributes_count_from(tupdesc); 649 | rmsg.old_tuple = 650 | palloc(sizeof(Decoderbufs__DatumMessage*) * rmsg.n_old_tuple); 651 | tuple_to_tuple_msg(rmsg.old_tuple, relation, 652 | TUPLE_ACCESS(change->data.tp.oldtuple), tupdesc); 653 | } 654 | if (change->data.tp.newtuple != NULL) { 655 | elog(DEBUG1, "decoding new tuple information"); 656 | tupdesc = RelationGetDescr(relation); 657 | 658 | rmsg.n_new_tuple = valid_attributes_count_from(tupdesc); 659 | rmsg.new_tuple = 660 | palloc(sizeof(Decoderbufs__DatumMessage*) * rmsg.n_new_tuple); 661 | tuple_to_tuple_msg(rmsg.new_tuple, relation, 662 | TUPLE_ACCESS(change->data.tp.newtuple), tupdesc); 663 | 664 | rmsg.n_new_typeinfo = rmsg.n_new_tuple; 665 | rmsg.new_typeinfo = 666 | palloc(sizeof(Decoderbufs__TypeInfo*) * rmsg.n_new_typeinfo); 667 | add_metadata_to_msg(rmsg.new_typeinfo, relation, 668 | TUPLE_ACCESS(change->data.tp.newtuple), tupdesc); 669 | } 670 | } 671 | break; 672 | case REORDER_BUFFER_CHANGE_DELETE: 673 | rmsg.op = DECODERBUFS__OP__DELETE; 674 | rmsg.has_op = true; 675 | elog(DEBUG1, "decoding DELETE for table %s; relation is %s", rmsg.table, selectiveInfo); 676 | /* if there was no PK, we only know that a delete happened */ 677 | if (!is_rel_non_selective && change->data.tp.oldtuple != NULL) { 678 | elog(DEBUG1, "decoding old tuple information"); 679 | tupdesc = RelationGetDescr(relation); 680 | rmsg.n_old_tuple = valid_attributes_count_from(tupdesc); 681 | rmsg.old_tuple = 682 | palloc(sizeof(Decoderbufs__DatumMessage*) * rmsg.n_old_tuple); 683 | tuple_to_tuple_msg(rmsg.old_tuple, relation, 684 | TUPLE_ACCESS(change->data.tp.oldtuple), tupdesc); 685 | } else { 686 | elog(DEBUG1, "no information to decode from DELETE because either no PK is present or REPLICA IDENTITY NOTHING or invalid "); 687 | } 688 | break; 689 | default: 690 | elog(WARNING, "unknown change action"); 691 | Assert(0); 692 | break; 693 | } 694 | 695 | /* write msg */ 696 | OutputPluginPrepareWrite(ctx, true); 697 | if (data->debug_mode) { 698 | //protobuf_c_text_to_string(ctx->out, (ProtobufCMessage*)&rmsg); 699 | print_row_msg(ctx->out, &rmsg); 700 | } else { 701 | size_t psize = decoderbufs__row_message__get_packed_size(&rmsg); 702 | void *packed = palloc(psize); 703 | size_t ssize = decoderbufs__row_message__pack(&rmsg, packed); 704 | appendBinaryStringInfo(ctx->out, packed, ssize); 705 | } 706 | OutputPluginWrite(ctx, true); 707 | 708 | /* Cleanup, freeing memory */ 709 | MemoryContextSwitchTo(old); 710 | MemoryContextReset(data->context); 711 | } 712 | -------------------------------------------------------------------------------- /src/proto/pg_logicaldec.pb-c.c: -------------------------------------------------------------------------------- 1 | /* Generated by the protocol buffer compiler. DO NOT EDIT! */ 2 | /* Generated from: pg_logicaldec.proto */ 3 | 4 | /* Do not generate deprecated warnings for self */ 5 | #ifndef PROTOBUF_C__NO_DEPRECATED 6 | #define PROTOBUF_C__NO_DEPRECATED 7 | #endif 8 | 9 | #include "pg_logicaldec.pb-c.h" 10 | void decoderbufs__point__init 11 | (Decoderbufs__Point *message) 12 | { 13 | static Decoderbufs__Point init_value = DECODERBUFS__POINT__INIT; 14 | *message = init_value; 15 | } 16 | size_t decoderbufs__point__get_packed_size 17 | (const Decoderbufs__Point *message) 18 | { 19 | assert(message->base.descriptor == &decoderbufs__point__descriptor); 20 | return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); 21 | } 22 | size_t decoderbufs__point__pack 23 | (const Decoderbufs__Point *message, 24 | uint8_t *out) 25 | { 26 | assert(message->base.descriptor == &decoderbufs__point__descriptor); 27 | return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); 28 | } 29 | size_t decoderbufs__point__pack_to_buffer 30 | (const Decoderbufs__Point *message, 31 | ProtobufCBuffer *buffer) 32 | { 33 | assert(message->base.descriptor == &decoderbufs__point__descriptor); 34 | return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); 35 | } 36 | Decoderbufs__Point * 37 | decoderbufs__point__unpack 38 | (ProtobufCAllocator *allocator, 39 | size_t len, 40 | const uint8_t *data) 41 | { 42 | return (Decoderbufs__Point *) 43 | protobuf_c_message_unpack (&decoderbufs__point__descriptor, 44 | allocator, len, data); 45 | } 46 | void decoderbufs__point__free_unpacked 47 | (Decoderbufs__Point *message, 48 | ProtobufCAllocator *allocator) 49 | { 50 | assert(message->base.descriptor == &decoderbufs__point__descriptor); 51 | protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); 52 | } 53 | void decoderbufs__datum_message__init 54 | (Decoderbufs__DatumMessage *message) 55 | { 56 | static Decoderbufs__DatumMessage init_value = DECODERBUFS__DATUM_MESSAGE__INIT; 57 | *message = init_value; 58 | } 59 | size_t decoderbufs__datum_message__get_packed_size 60 | (const Decoderbufs__DatumMessage *message) 61 | { 62 | assert(message->base.descriptor == &decoderbufs__datum_message__descriptor); 63 | return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); 64 | } 65 | size_t decoderbufs__datum_message__pack 66 | (const Decoderbufs__DatumMessage *message, 67 | uint8_t *out) 68 | { 69 | assert(message->base.descriptor == &decoderbufs__datum_message__descriptor); 70 | return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); 71 | } 72 | size_t decoderbufs__datum_message__pack_to_buffer 73 | (const Decoderbufs__DatumMessage *message, 74 | ProtobufCBuffer *buffer) 75 | { 76 | assert(message->base.descriptor == &decoderbufs__datum_message__descriptor); 77 | return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); 78 | } 79 | Decoderbufs__DatumMessage * 80 | decoderbufs__datum_message__unpack 81 | (ProtobufCAllocator *allocator, 82 | size_t len, 83 | const uint8_t *data) 84 | { 85 | return (Decoderbufs__DatumMessage *) 86 | protobuf_c_message_unpack (&decoderbufs__datum_message__descriptor, 87 | allocator, len, data); 88 | } 89 | void decoderbufs__datum_message__free_unpacked 90 | (Decoderbufs__DatumMessage *message, 91 | ProtobufCAllocator *allocator) 92 | { 93 | assert(message->base.descriptor == &decoderbufs__datum_message__descriptor); 94 | protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); 95 | } 96 | void decoderbufs__type_info__init 97 | (Decoderbufs__TypeInfo *message) 98 | { 99 | static Decoderbufs__TypeInfo init_value = DECODERBUFS__TYPE_INFO__INIT; 100 | *message = init_value; 101 | } 102 | size_t decoderbufs__type_info__get_packed_size 103 | (const Decoderbufs__TypeInfo *message) 104 | { 105 | assert(message->base.descriptor == &decoderbufs__type_info__descriptor); 106 | return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); 107 | } 108 | size_t decoderbufs__type_info__pack 109 | (const Decoderbufs__TypeInfo *message, 110 | uint8_t *out) 111 | { 112 | assert(message->base.descriptor == &decoderbufs__type_info__descriptor); 113 | return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); 114 | } 115 | size_t decoderbufs__type_info__pack_to_buffer 116 | (const Decoderbufs__TypeInfo *message, 117 | ProtobufCBuffer *buffer) 118 | { 119 | assert(message->base.descriptor == &decoderbufs__type_info__descriptor); 120 | return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); 121 | } 122 | Decoderbufs__TypeInfo * 123 | decoderbufs__type_info__unpack 124 | (ProtobufCAllocator *allocator, 125 | size_t len, 126 | const uint8_t *data) 127 | { 128 | return (Decoderbufs__TypeInfo *) 129 | protobuf_c_message_unpack (&decoderbufs__type_info__descriptor, 130 | allocator, len, data); 131 | } 132 | void decoderbufs__type_info__free_unpacked 133 | (Decoderbufs__TypeInfo *message, 134 | ProtobufCAllocator *allocator) 135 | { 136 | assert(message->base.descriptor == &decoderbufs__type_info__descriptor); 137 | protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); 138 | } 139 | void decoderbufs__row_message__init 140 | (Decoderbufs__RowMessage *message) 141 | { 142 | static Decoderbufs__RowMessage init_value = DECODERBUFS__ROW_MESSAGE__INIT; 143 | *message = init_value; 144 | } 145 | size_t decoderbufs__row_message__get_packed_size 146 | (const Decoderbufs__RowMessage *message) 147 | { 148 | assert(message->base.descriptor == &decoderbufs__row_message__descriptor); 149 | return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); 150 | } 151 | size_t decoderbufs__row_message__pack 152 | (const Decoderbufs__RowMessage *message, 153 | uint8_t *out) 154 | { 155 | assert(message->base.descriptor == &decoderbufs__row_message__descriptor); 156 | return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); 157 | } 158 | size_t decoderbufs__row_message__pack_to_buffer 159 | (const Decoderbufs__RowMessage *message, 160 | ProtobufCBuffer *buffer) 161 | { 162 | assert(message->base.descriptor == &decoderbufs__row_message__descriptor); 163 | return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); 164 | } 165 | Decoderbufs__RowMessage * 166 | decoderbufs__row_message__unpack 167 | (ProtobufCAllocator *allocator, 168 | size_t len, 169 | const uint8_t *data) 170 | { 171 | return (Decoderbufs__RowMessage *) 172 | protobuf_c_message_unpack (&decoderbufs__row_message__descriptor, 173 | allocator, len, data); 174 | } 175 | void decoderbufs__row_message__free_unpacked 176 | (Decoderbufs__RowMessage *message, 177 | ProtobufCAllocator *allocator) 178 | { 179 | assert(message->base.descriptor == &decoderbufs__row_message__descriptor); 180 | protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); 181 | } 182 | static const ProtobufCFieldDescriptor decoderbufs__point__field_descriptors[2] = 183 | { 184 | { 185 | "x", 186 | 1, 187 | PROTOBUF_C_LABEL_REQUIRED, 188 | PROTOBUF_C_TYPE_DOUBLE, 189 | 0, /* quantifier_offset */ 190 | offsetof(Decoderbufs__Point, x), 191 | NULL, 192 | NULL, 193 | 0, /* flags */ 194 | 0,NULL,NULL /* reserved1,reserved2, etc */ 195 | }, 196 | { 197 | "y", 198 | 2, 199 | PROTOBUF_C_LABEL_REQUIRED, 200 | PROTOBUF_C_TYPE_DOUBLE, 201 | 0, /* quantifier_offset */ 202 | offsetof(Decoderbufs__Point, y), 203 | NULL, 204 | NULL, 205 | 0, /* flags */ 206 | 0,NULL,NULL /* reserved1,reserved2, etc */ 207 | }, 208 | }; 209 | static const unsigned decoderbufs__point__field_indices_by_name[] = { 210 | 0, /* field[0] = x */ 211 | 1, /* field[1] = y */ 212 | }; 213 | static const ProtobufCIntRange decoderbufs__point__number_ranges[1 + 1] = 214 | { 215 | { 1, 0 }, 216 | { 0, 2 } 217 | }; 218 | const ProtobufCMessageDescriptor decoderbufs__point__descriptor = 219 | { 220 | PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, 221 | "decoderbufs.Point", 222 | "Point", 223 | "Decoderbufs__Point", 224 | "decoderbufs", 225 | sizeof(Decoderbufs__Point), 226 | 2, 227 | decoderbufs__point__field_descriptors, 228 | decoderbufs__point__field_indices_by_name, 229 | 1, decoderbufs__point__number_ranges, 230 | (ProtobufCMessageInit) decoderbufs__point__init, 231 | NULL,NULL,NULL /* reserved[123] */ 232 | }; 233 | static const ProtobufCFieldDescriptor decoderbufs__datum_message__field_descriptors[11] = 234 | { 235 | { 236 | "column_name", 237 | 1, 238 | PROTOBUF_C_LABEL_OPTIONAL, 239 | PROTOBUF_C_TYPE_STRING, 240 | 0, /* quantifier_offset */ 241 | offsetof(Decoderbufs__DatumMessage, column_name), 242 | NULL, 243 | NULL, 244 | 0, /* flags */ 245 | 0,NULL,NULL /* reserved1,reserved2, etc */ 246 | }, 247 | { 248 | "column_type", 249 | 2, 250 | PROTOBUF_C_LABEL_OPTIONAL, 251 | PROTOBUF_C_TYPE_INT64, 252 | offsetof(Decoderbufs__DatumMessage, has_column_type), 253 | offsetof(Decoderbufs__DatumMessage, column_type), 254 | NULL, 255 | NULL, 256 | 0, /* flags */ 257 | 0,NULL,NULL /* reserved1,reserved2, etc */ 258 | }, 259 | { 260 | "datum_int32", 261 | 3, 262 | PROTOBUF_C_LABEL_OPTIONAL, 263 | PROTOBUF_C_TYPE_INT32, 264 | offsetof(Decoderbufs__DatumMessage, datum_case), 265 | offsetof(Decoderbufs__DatumMessage, datum_int32), 266 | NULL, 267 | NULL, 268 | 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ 269 | 0,NULL,NULL /* reserved1,reserved2, etc */ 270 | }, 271 | { 272 | "datum_int64", 273 | 4, 274 | PROTOBUF_C_LABEL_OPTIONAL, 275 | PROTOBUF_C_TYPE_INT64, 276 | offsetof(Decoderbufs__DatumMessage, datum_case), 277 | offsetof(Decoderbufs__DatumMessage, datum_int64), 278 | NULL, 279 | NULL, 280 | 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ 281 | 0,NULL,NULL /* reserved1,reserved2, etc */ 282 | }, 283 | { 284 | "datum_float", 285 | 5, 286 | PROTOBUF_C_LABEL_OPTIONAL, 287 | PROTOBUF_C_TYPE_FLOAT, 288 | offsetof(Decoderbufs__DatumMessage, datum_case), 289 | offsetof(Decoderbufs__DatumMessage, datum_float), 290 | NULL, 291 | NULL, 292 | 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ 293 | 0,NULL,NULL /* reserved1,reserved2, etc */ 294 | }, 295 | { 296 | "datum_double", 297 | 6, 298 | PROTOBUF_C_LABEL_OPTIONAL, 299 | PROTOBUF_C_TYPE_DOUBLE, 300 | offsetof(Decoderbufs__DatumMessage, datum_case), 301 | offsetof(Decoderbufs__DatumMessage, datum_double), 302 | NULL, 303 | NULL, 304 | 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ 305 | 0,NULL,NULL /* reserved1,reserved2, etc */ 306 | }, 307 | { 308 | "datum_bool", 309 | 7, 310 | PROTOBUF_C_LABEL_OPTIONAL, 311 | PROTOBUF_C_TYPE_BOOL, 312 | offsetof(Decoderbufs__DatumMessage, datum_case), 313 | offsetof(Decoderbufs__DatumMessage, datum_bool), 314 | NULL, 315 | NULL, 316 | 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ 317 | 0,NULL,NULL /* reserved1,reserved2, etc */ 318 | }, 319 | { 320 | "datum_string", 321 | 8, 322 | PROTOBUF_C_LABEL_OPTIONAL, 323 | PROTOBUF_C_TYPE_STRING, 324 | offsetof(Decoderbufs__DatumMessage, datum_case), 325 | offsetof(Decoderbufs__DatumMessage, datum_string), 326 | NULL, 327 | NULL, 328 | 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ 329 | 0,NULL,NULL /* reserved1,reserved2, etc */ 330 | }, 331 | { 332 | "datum_bytes", 333 | 9, 334 | PROTOBUF_C_LABEL_OPTIONAL, 335 | PROTOBUF_C_TYPE_BYTES, 336 | offsetof(Decoderbufs__DatumMessage, datum_case), 337 | offsetof(Decoderbufs__DatumMessage, datum_bytes), 338 | NULL, 339 | NULL, 340 | 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ 341 | 0,NULL,NULL /* reserved1,reserved2, etc */ 342 | }, 343 | { 344 | "datum_point", 345 | 10, 346 | PROTOBUF_C_LABEL_OPTIONAL, 347 | PROTOBUF_C_TYPE_MESSAGE, 348 | offsetof(Decoderbufs__DatumMessage, datum_case), 349 | offsetof(Decoderbufs__DatumMessage, datum_point), 350 | &decoderbufs__point__descriptor, 351 | NULL, 352 | 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ 353 | 0,NULL,NULL /* reserved1,reserved2, etc */ 354 | }, 355 | { 356 | "datum_missing", 357 | 11, 358 | PROTOBUF_C_LABEL_OPTIONAL, 359 | PROTOBUF_C_TYPE_BOOL, 360 | offsetof(Decoderbufs__DatumMessage, datum_case), 361 | offsetof(Decoderbufs__DatumMessage, datum_missing), 362 | NULL, 363 | NULL, 364 | 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ 365 | 0,NULL,NULL /* reserved1,reserved2, etc */ 366 | }, 367 | }; 368 | static const unsigned decoderbufs__datum_message__field_indices_by_name[] = { 369 | 0, /* field[0] = column_name */ 370 | 1, /* field[1] = column_type */ 371 | 6, /* field[6] = datum_bool */ 372 | 8, /* field[8] = datum_bytes */ 373 | 5, /* field[5] = datum_double */ 374 | 4, /* field[4] = datum_float */ 375 | 2, /* field[2] = datum_int32 */ 376 | 3, /* field[3] = datum_int64 */ 377 | 10, /* field[10] = datum_missing */ 378 | 9, /* field[9] = datum_point */ 379 | 7, /* field[7] = datum_string */ 380 | }; 381 | static const ProtobufCIntRange decoderbufs__datum_message__number_ranges[1 + 1] = 382 | { 383 | { 1, 0 }, 384 | { 0, 11 } 385 | }; 386 | const ProtobufCMessageDescriptor decoderbufs__datum_message__descriptor = 387 | { 388 | PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, 389 | "decoderbufs.DatumMessage", 390 | "DatumMessage", 391 | "Decoderbufs__DatumMessage", 392 | "decoderbufs", 393 | sizeof(Decoderbufs__DatumMessage), 394 | 11, 395 | decoderbufs__datum_message__field_descriptors, 396 | decoderbufs__datum_message__field_indices_by_name, 397 | 1, decoderbufs__datum_message__number_ranges, 398 | (ProtobufCMessageInit) decoderbufs__datum_message__init, 399 | NULL,NULL,NULL /* reserved[123] */ 400 | }; 401 | static const ProtobufCFieldDescriptor decoderbufs__type_info__field_descriptors[2] = 402 | { 403 | { 404 | "modifier", 405 | 1, 406 | PROTOBUF_C_LABEL_REQUIRED, 407 | PROTOBUF_C_TYPE_STRING, 408 | 0, /* quantifier_offset */ 409 | offsetof(Decoderbufs__TypeInfo, modifier), 410 | NULL, 411 | NULL, 412 | 0, /* flags */ 413 | 0,NULL,NULL /* reserved1,reserved2, etc */ 414 | }, 415 | { 416 | "value_optional", 417 | 2, 418 | PROTOBUF_C_LABEL_REQUIRED, 419 | PROTOBUF_C_TYPE_BOOL, 420 | 0, /* quantifier_offset */ 421 | offsetof(Decoderbufs__TypeInfo, value_optional), 422 | NULL, 423 | NULL, 424 | 0, /* flags */ 425 | 0,NULL,NULL /* reserved1,reserved2, etc */ 426 | }, 427 | }; 428 | static const unsigned decoderbufs__type_info__field_indices_by_name[] = { 429 | 0, /* field[0] = modifier */ 430 | 1, /* field[1] = value_optional */ 431 | }; 432 | static const ProtobufCIntRange decoderbufs__type_info__number_ranges[1 + 1] = 433 | { 434 | { 1, 0 }, 435 | { 0, 2 } 436 | }; 437 | const ProtobufCMessageDescriptor decoderbufs__type_info__descriptor = 438 | { 439 | PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, 440 | "decoderbufs.TypeInfo", 441 | "TypeInfo", 442 | "Decoderbufs__TypeInfo", 443 | "decoderbufs", 444 | sizeof(Decoderbufs__TypeInfo), 445 | 2, 446 | decoderbufs__type_info__field_descriptors, 447 | decoderbufs__type_info__field_indices_by_name, 448 | 1, decoderbufs__type_info__number_ranges, 449 | (ProtobufCMessageInit) decoderbufs__type_info__init, 450 | NULL,NULL,NULL /* reserved[123] */ 451 | }; 452 | static const ProtobufCFieldDescriptor decoderbufs__row_message__field_descriptors[7] = 453 | { 454 | { 455 | "transaction_id", 456 | 1, 457 | PROTOBUF_C_LABEL_OPTIONAL, 458 | PROTOBUF_C_TYPE_UINT32, 459 | offsetof(Decoderbufs__RowMessage, has_transaction_id), 460 | offsetof(Decoderbufs__RowMessage, transaction_id), 461 | NULL, 462 | NULL, 463 | 0, /* flags */ 464 | 0,NULL,NULL /* reserved1,reserved2, etc */ 465 | }, 466 | { 467 | "commit_time", 468 | 2, 469 | PROTOBUF_C_LABEL_OPTIONAL, 470 | PROTOBUF_C_TYPE_UINT64, 471 | offsetof(Decoderbufs__RowMessage, has_commit_time), 472 | offsetof(Decoderbufs__RowMessage, commit_time), 473 | NULL, 474 | NULL, 475 | 0, /* flags */ 476 | 0,NULL,NULL /* reserved1,reserved2, etc */ 477 | }, 478 | { 479 | "table", 480 | 3, 481 | PROTOBUF_C_LABEL_OPTIONAL, 482 | PROTOBUF_C_TYPE_STRING, 483 | 0, /* quantifier_offset */ 484 | offsetof(Decoderbufs__RowMessage, table), 485 | NULL, 486 | NULL, 487 | 0, /* flags */ 488 | 0,NULL,NULL /* reserved1,reserved2, etc */ 489 | }, 490 | { 491 | "op", 492 | 4, 493 | PROTOBUF_C_LABEL_OPTIONAL, 494 | PROTOBUF_C_TYPE_ENUM, 495 | offsetof(Decoderbufs__RowMessage, has_op), 496 | offsetof(Decoderbufs__RowMessage, op), 497 | &decoderbufs__op__descriptor, 498 | NULL, 499 | 0, /* flags */ 500 | 0,NULL,NULL /* reserved1,reserved2, etc */ 501 | }, 502 | { 503 | "new_tuple", 504 | 5, 505 | PROTOBUF_C_LABEL_REPEATED, 506 | PROTOBUF_C_TYPE_MESSAGE, 507 | offsetof(Decoderbufs__RowMessage, n_new_tuple), 508 | offsetof(Decoderbufs__RowMessage, new_tuple), 509 | &decoderbufs__datum_message__descriptor, 510 | NULL, 511 | 0, /* flags */ 512 | 0,NULL,NULL /* reserved1,reserved2, etc */ 513 | }, 514 | { 515 | "old_tuple", 516 | 6, 517 | PROTOBUF_C_LABEL_REPEATED, 518 | PROTOBUF_C_TYPE_MESSAGE, 519 | offsetof(Decoderbufs__RowMessage, n_old_tuple), 520 | offsetof(Decoderbufs__RowMessage, old_tuple), 521 | &decoderbufs__datum_message__descriptor, 522 | NULL, 523 | 0, /* flags */ 524 | 0,NULL,NULL /* reserved1,reserved2, etc */ 525 | }, 526 | { 527 | "new_typeinfo", 528 | 7, 529 | PROTOBUF_C_LABEL_REPEATED, 530 | PROTOBUF_C_TYPE_MESSAGE, 531 | offsetof(Decoderbufs__RowMessage, n_new_typeinfo), 532 | offsetof(Decoderbufs__RowMessage, new_typeinfo), 533 | &decoderbufs__type_info__descriptor, 534 | NULL, 535 | 0, /* flags */ 536 | 0,NULL,NULL /* reserved1,reserved2, etc */ 537 | }, 538 | }; 539 | static const unsigned decoderbufs__row_message__field_indices_by_name[] = { 540 | 1, /* field[1] = commit_time */ 541 | 4, /* field[4] = new_tuple */ 542 | 6, /* field[6] = new_typeinfo */ 543 | 5, /* field[5] = old_tuple */ 544 | 3, /* field[3] = op */ 545 | 2, /* field[2] = table */ 546 | 0, /* field[0] = transaction_id */ 547 | }; 548 | static const ProtobufCIntRange decoderbufs__row_message__number_ranges[1 + 1] = 549 | { 550 | { 1, 0 }, 551 | { 0, 7 } 552 | }; 553 | const ProtobufCMessageDescriptor decoderbufs__row_message__descriptor = 554 | { 555 | PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, 556 | "decoderbufs.RowMessage", 557 | "RowMessage", 558 | "Decoderbufs__RowMessage", 559 | "decoderbufs", 560 | sizeof(Decoderbufs__RowMessage), 561 | 7, 562 | decoderbufs__row_message__field_descriptors, 563 | decoderbufs__row_message__field_indices_by_name, 564 | 1, decoderbufs__row_message__number_ranges, 565 | (ProtobufCMessageInit) decoderbufs__row_message__init, 566 | NULL,NULL,NULL /* reserved[123] */ 567 | }; 568 | static const ProtobufCEnumValue decoderbufs__op__enum_values_by_number[6] = 569 | { 570 | { "UNKNOWN", "DECODERBUFS__OP__UNKNOWN", -1 }, 571 | { "INSERT", "DECODERBUFS__OP__INSERT", 0 }, 572 | { "UPDATE", "DECODERBUFS__OP__UPDATE", 1 }, 573 | { "DELETE", "DECODERBUFS__OP__DELETE", 2 }, 574 | { "BEGIN", "DECODERBUFS__OP__BEGIN", 3 }, 575 | { "COMMIT", "DECODERBUFS__OP__COMMIT", 4 }, 576 | }; 577 | static const ProtobufCIntRange decoderbufs__op__value_ranges[] = { 578 | {-1, 0},{0, 6} 579 | }; 580 | static const ProtobufCEnumValueIndex decoderbufs__op__enum_values_by_name[6] = 581 | { 582 | { "BEGIN", 4 }, 583 | { "COMMIT", 5 }, 584 | { "DELETE", 3 }, 585 | { "INSERT", 1 }, 586 | { "UNKNOWN", 0 }, 587 | { "UPDATE", 2 }, 588 | }; 589 | const ProtobufCEnumDescriptor decoderbufs__op__descriptor = 590 | { 591 | PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC, 592 | "decoderbufs.Op", 593 | "Op", 594 | "Decoderbufs__Op", 595 | "decoderbufs", 596 | 6, 597 | decoderbufs__op__enum_values_by_number, 598 | 6, 599 | decoderbufs__op__enum_values_by_name, 600 | 1, 601 | decoderbufs__op__value_ranges, 602 | NULL,NULL,NULL,NULL /* reserved[1234] */ 603 | }; 604 | -------------------------------------------------------------------------------- /src/proto/pg_logicaldec.pb-c.h: -------------------------------------------------------------------------------- 1 | /* Generated by the protocol buffer compiler. DO NOT EDIT! */ 2 | /* Generated from: pg_logicaldec.proto */ 3 | 4 | #ifndef PROTOBUF_C_pg_5flogicaldec_2eproto__INCLUDED 5 | #define PROTOBUF_C_pg_5flogicaldec_2eproto__INCLUDED 6 | 7 | #include 8 | 9 | PROTOBUF_C__BEGIN_DECLS 10 | 11 | #if PROTOBUF_C_VERSION_NUMBER < 1000000 12 | # error This file was generated by a newer version of protoc-c which is incompatible with your libprotobuf-c headers. Please update your headers. 13 | #elif 1002001 < PROTOBUF_C_MIN_COMPILER_VERSION 14 | # error This file was generated by an older version of protoc-c which is incompatible with your libprotobuf-c headers. Please regenerate this file with a newer version of protoc-c. 15 | #endif 16 | 17 | 18 | typedef struct _Decoderbufs__Point Decoderbufs__Point; 19 | typedef struct _Decoderbufs__DatumMessage Decoderbufs__DatumMessage; 20 | typedef struct _Decoderbufs__TypeInfo Decoderbufs__TypeInfo; 21 | typedef struct _Decoderbufs__RowMessage Decoderbufs__RowMessage; 22 | 23 | 24 | /* --- enums --- */ 25 | 26 | typedef enum _Decoderbufs__Op { 27 | DECODERBUFS__OP__UNKNOWN = -1, 28 | DECODERBUFS__OP__INSERT = 0, 29 | DECODERBUFS__OP__UPDATE = 1, 30 | DECODERBUFS__OP__DELETE = 2, 31 | DECODERBUFS__OP__BEGIN = 3, 32 | DECODERBUFS__OP__COMMIT = 4 33 | PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(DECODERBUFS__OP) 34 | } Decoderbufs__Op; 35 | 36 | /* --- messages --- */ 37 | 38 | struct _Decoderbufs__Point 39 | { 40 | ProtobufCMessage base; 41 | double x; 42 | double y; 43 | }; 44 | #define DECODERBUFS__POINT__INIT \ 45 | { PROTOBUF_C_MESSAGE_INIT (&decoderbufs__point__descriptor) \ 46 | , 0, 0 } 47 | 48 | 49 | typedef enum { 50 | DECODERBUFS__DATUM_MESSAGE__DATUM__NOT_SET = 0, 51 | DECODERBUFS__DATUM_MESSAGE__DATUM_DATUM_INT32 = 3, 52 | DECODERBUFS__DATUM_MESSAGE__DATUM_DATUM_INT64 = 4, 53 | DECODERBUFS__DATUM_MESSAGE__DATUM_DATUM_FLOAT = 5, 54 | DECODERBUFS__DATUM_MESSAGE__DATUM_DATUM_DOUBLE = 6, 55 | DECODERBUFS__DATUM_MESSAGE__DATUM_DATUM_BOOL = 7, 56 | DECODERBUFS__DATUM_MESSAGE__DATUM_DATUM_STRING = 8, 57 | DECODERBUFS__DATUM_MESSAGE__DATUM_DATUM_BYTES = 9, 58 | DECODERBUFS__DATUM_MESSAGE__DATUM_DATUM_POINT = 10, 59 | DECODERBUFS__DATUM_MESSAGE__DATUM_DATUM_MISSING = 11, 60 | } Decoderbufs__DatumMessage__DatumCase; 61 | 62 | struct _Decoderbufs__DatumMessage 63 | { 64 | ProtobufCMessage base; 65 | char *column_name; 66 | protobuf_c_boolean has_column_type; 67 | int64_t column_type; 68 | Decoderbufs__DatumMessage__DatumCase datum_case; 69 | union { 70 | int32_t datum_int32; 71 | int64_t datum_int64; 72 | float datum_float; 73 | double datum_double; 74 | protobuf_c_boolean datum_bool; 75 | char *datum_string; 76 | ProtobufCBinaryData datum_bytes; 77 | Decoderbufs__Point *datum_point; 78 | protobuf_c_boolean datum_missing; 79 | }; 80 | }; 81 | #define DECODERBUFS__DATUM_MESSAGE__INIT \ 82 | { PROTOBUF_C_MESSAGE_INIT (&decoderbufs__datum_message__descriptor) \ 83 | , NULL, 0,0, DECODERBUFS__DATUM_MESSAGE__DATUM__NOT_SET, {0} } 84 | 85 | 86 | struct _Decoderbufs__TypeInfo 87 | { 88 | ProtobufCMessage base; 89 | char *modifier; 90 | protobuf_c_boolean value_optional; 91 | }; 92 | #define DECODERBUFS__TYPE_INFO__INIT \ 93 | { PROTOBUF_C_MESSAGE_INIT (&decoderbufs__type_info__descriptor) \ 94 | , NULL, 0 } 95 | 96 | 97 | struct _Decoderbufs__RowMessage 98 | { 99 | ProtobufCMessage base; 100 | protobuf_c_boolean has_transaction_id; 101 | uint32_t transaction_id; 102 | protobuf_c_boolean has_commit_time; 103 | uint64_t commit_time; 104 | char *table; 105 | protobuf_c_boolean has_op; 106 | Decoderbufs__Op op; 107 | size_t n_new_tuple; 108 | Decoderbufs__DatumMessage **new_tuple; 109 | size_t n_old_tuple; 110 | Decoderbufs__DatumMessage **old_tuple; 111 | size_t n_new_typeinfo; 112 | Decoderbufs__TypeInfo **new_typeinfo; 113 | }; 114 | #define DECODERBUFS__ROW_MESSAGE__INIT \ 115 | { PROTOBUF_C_MESSAGE_INIT (&decoderbufs__row_message__descriptor) \ 116 | , 0,0, 0,0, NULL, 0,0, 0,NULL, 0,NULL, 0,NULL } 117 | 118 | 119 | /* Decoderbufs__Point methods */ 120 | void decoderbufs__point__init 121 | (Decoderbufs__Point *message); 122 | size_t decoderbufs__point__get_packed_size 123 | (const Decoderbufs__Point *message); 124 | size_t decoderbufs__point__pack 125 | (const Decoderbufs__Point *message, 126 | uint8_t *out); 127 | size_t decoderbufs__point__pack_to_buffer 128 | (const Decoderbufs__Point *message, 129 | ProtobufCBuffer *buffer); 130 | Decoderbufs__Point * 131 | decoderbufs__point__unpack 132 | (ProtobufCAllocator *allocator, 133 | size_t len, 134 | const uint8_t *data); 135 | void decoderbufs__point__free_unpacked 136 | (Decoderbufs__Point *message, 137 | ProtobufCAllocator *allocator); 138 | /* Decoderbufs__DatumMessage methods */ 139 | void decoderbufs__datum_message__init 140 | (Decoderbufs__DatumMessage *message); 141 | size_t decoderbufs__datum_message__get_packed_size 142 | (const Decoderbufs__DatumMessage *message); 143 | size_t decoderbufs__datum_message__pack 144 | (const Decoderbufs__DatumMessage *message, 145 | uint8_t *out); 146 | size_t decoderbufs__datum_message__pack_to_buffer 147 | (const Decoderbufs__DatumMessage *message, 148 | ProtobufCBuffer *buffer); 149 | Decoderbufs__DatumMessage * 150 | decoderbufs__datum_message__unpack 151 | (ProtobufCAllocator *allocator, 152 | size_t len, 153 | const uint8_t *data); 154 | void decoderbufs__datum_message__free_unpacked 155 | (Decoderbufs__DatumMessage *message, 156 | ProtobufCAllocator *allocator); 157 | /* Decoderbufs__TypeInfo methods */ 158 | void decoderbufs__type_info__init 159 | (Decoderbufs__TypeInfo *message); 160 | size_t decoderbufs__type_info__get_packed_size 161 | (const Decoderbufs__TypeInfo *message); 162 | size_t decoderbufs__type_info__pack 163 | (const Decoderbufs__TypeInfo *message, 164 | uint8_t *out); 165 | size_t decoderbufs__type_info__pack_to_buffer 166 | (const Decoderbufs__TypeInfo *message, 167 | ProtobufCBuffer *buffer); 168 | Decoderbufs__TypeInfo * 169 | decoderbufs__type_info__unpack 170 | (ProtobufCAllocator *allocator, 171 | size_t len, 172 | const uint8_t *data); 173 | void decoderbufs__type_info__free_unpacked 174 | (Decoderbufs__TypeInfo *message, 175 | ProtobufCAllocator *allocator); 176 | /* Decoderbufs__RowMessage methods */ 177 | void decoderbufs__row_message__init 178 | (Decoderbufs__RowMessage *message); 179 | size_t decoderbufs__row_message__get_packed_size 180 | (const Decoderbufs__RowMessage *message); 181 | size_t decoderbufs__row_message__pack 182 | (const Decoderbufs__RowMessage *message, 183 | uint8_t *out); 184 | size_t decoderbufs__row_message__pack_to_buffer 185 | (const Decoderbufs__RowMessage *message, 186 | ProtobufCBuffer *buffer); 187 | Decoderbufs__RowMessage * 188 | decoderbufs__row_message__unpack 189 | (ProtobufCAllocator *allocator, 190 | size_t len, 191 | const uint8_t *data); 192 | void decoderbufs__row_message__free_unpacked 193 | (Decoderbufs__RowMessage *message, 194 | ProtobufCAllocator *allocator); 195 | /* --- per-message closures --- */ 196 | 197 | typedef void (*Decoderbufs__Point_Closure) 198 | (const Decoderbufs__Point *message, 199 | void *closure_data); 200 | typedef void (*Decoderbufs__DatumMessage_Closure) 201 | (const Decoderbufs__DatumMessage *message, 202 | void *closure_data); 203 | typedef void (*Decoderbufs__TypeInfo_Closure) 204 | (const Decoderbufs__TypeInfo *message, 205 | void *closure_data); 206 | typedef void (*Decoderbufs__RowMessage_Closure) 207 | (const Decoderbufs__RowMessage *message, 208 | void *closure_data); 209 | 210 | /* --- services --- */ 211 | 212 | 213 | /* --- descriptors --- */ 214 | 215 | extern const ProtobufCEnumDescriptor decoderbufs__op__descriptor; 216 | extern const ProtobufCMessageDescriptor decoderbufs__point__descriptor; 217 | extern const ProtobufCMessageDescriptor decoderbufs__datum_message__descriptor; 218 | extern const ProtobufCMessageDescriptor decoderbufs__type_info__descriptor; 219 | extern const ProtobufCMessageDescriptor decoderbufs__row_message__descriptor; 220 | 221 | PROTOBUF_C__END_DECLS 222 | 223 | 224 | #endif /* PROTOBUF_C_pg_5flogicaldec_2eproto__INCLUDED */ 225 | --------------------------------------------------------------------------------