/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #include "postgres.h" #include "utils/agtype_raw.h" /* * Used for building an agtype container. */ struct agtype_build_state { int a_offset; // next location to write agtentry int i; // index of current agtentry being processed int d_start; // start of variable-length portion StringInfo buffer; }; /* * Define the type and size of the agt_header. * Copied from agtype_ext.c. */ #define AGT_HEADER_TYPE uint32 #define AGT_HEADER_SIZE sizeof(AGT_HEADER_TYPE) /* * Following macros are usable in the context where * agtype_build_state is available */ #define BUFFER_RESERVE(size) reserve_from_buffer(bstate->buffer, (size)) #define BUFFER_WRITE_PAD() pad_buffer_to_int(bstate->buffer) #define BUFFER_WRITE_CONST(offset, type, val) *((type *)(bstate->buffer->data + (offset))) = (val) #define BUFFER_WRITE_PTR(offset, ptr, len) memcpy(bstate->buffer->data + offset, ptr, len) static int write_pointer(agtype_build_state *bstate, char *ptr, int len); static void write_agtentry(agtype_build_state *bstate, agtentry agte); /* * Same as `write_ptr` except the content comes from * constant value instead of pointer. */ #define write_const(val, type) \ do \ { \ int len = sizeof(type); \ int offset = BUFFER_RESERVE(len); \ BUFFER_WRITE_CONST(offset, type, val); \ } \ while (0) #define write_ptr(ptr, len) write_pointer(bstate, ptr, len) #define write_agt(agte) write_agtentry(bstate, agte) /* * Copies the content of `ptr` to the tail of the buffer (variable-length * portion). */ static int write_pointer(agtype_build_state *bstate, char *ptr, int len) { int offset = BUFFER_RESERVE(len); BUFFER_WRITE_PTR(offset, ptr, len); return len; } /* * Copies the content of `agte` to the next available location in the agtentry * portion of the buffer. That location is pointed by `bstate->a_offset`. * * This function must be called after data is written to the variable-length * portion. */ static void write_agtentry(agtype_build_state *bstate, agtentry agte) { int totallen = bstate->buffer->len - bstate->d_start; /* * Bail out if total variable-length data exceeds what will fit in a * agtentry length field. We check this in each iteration, not just * once at the end, to forestall possible integer overflow. */ if (totallen > AGTENTRY_OFFLENMASK) { ereport( ERROR, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg( "total size of agtype array elements exceeds the maximum of %u bytes", AGTENTRY_OFFLENMASK))); } if (((bstate->i) % AGT_OFFSET_STRIDE) == 0) { agte = (agte & AGTENTRY_TYPEMASK) | totallen | AGTENTRY_HAS_OFF; } BUFFER_WRITE_CONST(bstate->a_offset, agtentry, agte); bstate->a_offset += sizeof(agtentry); } /* * `header_flag` = a valid agtype_container header field * `size` = size of container (number of pairs or elements) */ agtype_build_state *init_agtype_build_state(uint32 size, uint32 header_flag) { int agtentry_count; int agtentry_len; agtype_build_state *bstate; bstate = palloc0(sizeof(agtype_build_state)); bstate->buffer = makeStringInfo(); bstate->a_offset = 0; bstate->i = 0; // reserve for varlen header BUFFER_RESERVE(VARHDRSZ); bstate->a_offset += VARHDRSZ; // write container header BUFFER_RESERVE(sizeof(uint32)); BUFFER_WRITE_CONST(bstate->a_offset, uint32, header_flag | size); bstate->a_offset += sizeof(uint32); // reserve for agtentry headers if ((header_flag & AGT_FOBJECT) != 0) { agtentry_count = size * 2; } else if ((header_flag & AGT_FARRAY) != 0) { agtentry_count = size; } else { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Invalid container type."))); } agtentry_len = sizeof(agtentry) * agtentry_count; BUFFER_RESERVE(agtentry_len); bstate->d_start = bstate->a_offset + agtentry_len; return bstate; } agtype *build_agtype(agtype_build_state *bstate) { agtype *result = (agtype *) bstate->buffer->data; SET_VARSIZE(result, bstate->buffer->len); return result; } void pfree_agtype_build_state(agtype_build_state *bstate) { /* * bstate->buffer->data is not pfree'd because this pointer * is returned by the `build_agtype` function. */ pfree(bstate->buffer); pfree(bstate); } void write_string(agtype_build_state *bstate, char *str) { int length = strlen(str); write_ptr(str, length); write_agt(AGTENTRY_IS_STRING | length); bstate->i++; } void write_graphid(agtype_build_state *bstate, graphid graphid) { int length = 0; // padding length += BUFFER_WRITE_PAD(); // graphid header write_const(AGT_HEADER_INTEGER, AGT_HEADER_TYPE); length += AGT_HEADER_SIZE; // graphid value write_const(graphid, int64); length += sizeof(int64); // agtentry write_agt(AGTENTRY_IS_AGTYPE | length); bstate->i++; } void write_container(agtype_build_state *bstate, agtype *agtype) { int length = 0; // padding length += BUFFER_WRITE_PAD(); // varlen data length += write_ptr((char *) &agtype->root, VARSIZE(agtype)); // agtentry write_agt(AGTENTRY_IS_CONTAINER | length); bstate->i++; } /* * `val` = container of the extended type * `header` = AGT_HEADER_VERTEX, AGT_HEADER_EDGE or AGT_HEADER_PATH */ void write_extended(agtype_build_state *bstate, agtype *val, uint32 header) { int length = 0; // padding length += BUFFER_WRITE_PAD(); // vertex header write_const(header, AGT_HEADER_TYPE); length += AGT_HEADER_SIZE; // vertex data length += write_ptr((char *) &val->root, VARSIZE(val)); // agtentry write_agt(AGTENTRY_IS_AGTYPE | length); bstate->i++; }