Beispiel #1
0
/*
 * Appends a properly quoted CSV field to StringInfo.
 */
static void
append_valid_csv(StringInfoData *buffer, const char *appendStr)
{
	const char *pChar;

	/*
	 * If the append string is null then do nothing.  NULL fields are not
	 * quoted in CSV.
	 */
	if (appendStr == NULL)
		return;

	/* Only format for CSV if appendStr contains: ", comma, \n, \r */
	if (strstr(appendStr, ",") || strstr(appendStr, "\"") ||
		strstr(appendStr, "\n") || strstr(appendStr, "\r"))
	{
		appendStringInfoCharMacro(buffer, '"');

		for (pChar = appendStr; *pChar; pChar++)
		{
			if (*pChar == '"')	/* double single quotes */
				appendStringInfoCharMacro(buffer, *pChar);

			appendStringInfoCharMacro(buffer, *pChar);
		}

		appendStringInfoCharMacro(buffer, '"');
	}
	/* Else just append */
	else
		appendStringInfoString(buffer, appendStr);
}
Beispiel #2
0
/*
 * Copy from s (for source) to r (for result), wrapping with q (quote)
 * characters and doubling any quote characters found.
 */
static void
strcpy_quoted(StringInfo r, const char *s, const char q)
{
	appendStringInfoCharMacro(r, q);
	while (*s)
	{
		if (*s == q)
			appendStringInfoCharMacro(r, q);
		appendStringInfoCharMacro(r, *s);
		s++;
	}
	appendStringInfoCharMacro(r, q);
}
Beispiel #3
0
char *lookup_analysis_thing(MemoryContext cxt, char *thing) {
    char       *definition = "";
    StringInfo query;

    SPI_connect();

    query = makeStringInfo();
    appendStringInfo(query, "select (to_json(name) || ':' || definition) from %s;", TextDatumGetCString(DirectFunctionCall1(quote_ident, CStringGetTextDatum(thing))));

    if (SPI_execute(query->data, true, 0) != SPI_OK_SELECT)
        elog(ERROR, "Problem looking up analysis thing with query: %s", query->data);

    if (SPI_processed > 0) {
        StringInfo json = makeStringInfo();
        int        i;

        for (i = 0; i < SPI_processed; i++) {
            if (i > 0) appendStringInfoCharMacro(json, ',');
            appendStringInfo(json, "%s", SPI_getvalue(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 1));
        }
        definition = (char *) MemoryContextAllocZero(cxt, (Size) json->len + 1);
        memcpy(definition, json->data, json->len);
    }

    SPI_finish();

    return definition;
}
Beispiel #4
0
/*
 * Produce a JSON string literal, properly escaping characters in the text.
 */
void
escape_json(StringInfo buf, const char *str)
{
	const char *p;

	appendStringInfoCharMacro(buf, '\"');
	for (p = str; *p; p++)
	{
		switch (*p)
		{
			case '\b':
				appendStringInfoString(buf, "\\b");
				break;
			case '\f':
				appendStringInfoString(buf, "\\f");
				break;
			case '\n':
				appendStringInfoString(buf, "\\n");
				break;
			case '\r':
				appendStringInfoString(buf, "\\r");
				break;
			case '\t':
				appendStringInfoString(buf, "\\t");
				break;
			case '"':
				appendStringInfoString(buf, "\\\"");
				break;
			case '\\':
				appendStringInfoString(buf, "\\\\");
				break;
			default:
				if ((unsigned char) *p < ' ')
					appendStringInfo(buf, "\\u%04x", (int) *p);
				else
					appendStringInfoCharMacro(buf, *p);
				break;
		}
	}
	appendStringInfoCharMacro(buf, '\"');
}
static void
add_indent(StringInfo out, bool indent, int level)
{
	if (indent)
	{
		int			i;

		appendStringInfoCharMacro(out, '\n');
		for (i = 0; i < level; i++)
			appendBinaryStringInfo(out, "    ", 4);
	}
}
Beispiel #6
0
void appendBinaryStringInfoAndStripLineBreaks(StringInfo str, const char *data, int datalen) {
    int i;
    Assert(str != NULL);

    /* Make more room if needed */
    enlargeStringInfo(str, datalen+1);

    /* OK, append the data */
    for (i=0; i<datalen; i++) {
        char ch = data[i];
        switch (ch) {
            case '\r':
            case '\n':
                appendStringInfoCharMacro(str, ' ');
                break;
            default:
                appendStringInfoCharMacro(str, ch);
                break;
        }
    }
}
Beispiel #7
0
StringInfo
quote_variant_name_cstring(const char *variant_name)
{
    StringInfo			out = makeStringInfo();
    bool						need_quote;
    const char			*tmp;

    appendStringInfoChar(out, '(');
    need_quote = false;
    for (tmp = variant_name; *tmp; tmp++)
    {
        const char		ch = *tmp;

        if (ch == '"' || ch == '(' || ch == ')' || ch == ',' ||
                isspace((unsigned char) ch))
        {
            need_quote = true;
            break;
        }
    }
    if(!need_quote)
        appendStringInfoString(out, variant_name);
    else
    {
        appendStringInfoChar(out, '"');
        for (tmp = variant_name; *tmp; tmp++)
        {
            const char		ch = *tmp;

            if (ch == '"')
                appendStringInfoCharMacro(out, ch);
            appendStringInfoCharMacro(out, ch);
        }
        appendStringInfoChar(out, '"');
    }
    appendStringInfoChar(out, ')');

    return out;
}
Beispiel #8
0
/*
 * JsonbToCString
 *	   Converts jsonb value to a C-string.
 *
 * If 'out' argument is non-null, the resulting C-string is stored inside the
 * StringBuffer.  The resulting string is always returned.
 *
 * A typical case for passing the StringInfo in rather than NULL is where the
 * caller wants access to the len attribute without having to call strlen, e.g.
 * if they are converting it to a text* object.
 */
char *
JsonbToCString(StringInfo out, JsonbSuperHeader in, int estimated_len)
{
	bool		first = true;
	JsonbIterator *it;
	int			type = 0;
	JsonbValue	v;
	int			level = 0;
	bool		redo_switch = false;

	if (out == NULL)
		out = makeStringInfo();

	enlargeStringInfo(out, (estimated_len >= 0) ? estimated_len : 64);

	it = JsonbIteratorInit(in);

	while (redo_switch ||
		   ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE))
	{
		redo_switch = false;
		switch (type)
		{
			case WJB_BEGIN_ARRAY:
				if (!first)
					appendBinaryStringInfo(out, ", ", 2);
				first = true;

				if (!v.val.array.rawScalar)
					appendStringInfoChar(out, '[');
				level++;
				break;
			case WJB_BEGIN_OBJECT:
				if (!first)
					appendBinaryStringInfo(out, ", ", 2);
				first = true;
				appendStringInfoCharMacro(out, '{');

				level++;
				break;
			case WJB_KEY:
				if (!first)
					appendBinaryStringInfo(out, ", ", 2);
				first = true;

				/* json rules guarantee this is a string */
				jsonb_put_escaped_value(out, &v);
				appendBinaryStringInfo(out, ": ", 2);

				type = JsonbIteratorNext(&it, &v, false);
				if (type == WJB_VALUE)
				{
					first = false;
					jsonb_put_escaped_value(out, &v);
				}
				else
				{
					Assert(type == WJB_BEGIN_OBJECT || type == WJB_BEGIN_ARRAY);

					/*
					 * We need to rerun the current switch() since we need to
					 * output the object which we just got from the iterator
					 * before calling the iterator again.
					 */
					redo_switch = true;
				}
				break;
			case WJB_ELEM:
				if (!first)
					appendBinaryStringInfo(out, ", ", 2);
				else
					first = false;

				jsonb_put_escaped_value(out, &v);
				break;
			case WJB_END_ARRAY:
				level--;
				if (!v.val.array.rawScalar)
					appendStringInfoChar(out, ']');
				first = false;
				break;
			case WJB_END_OBJECT:
				level--;
				appendStringInfoCharMacro(out, '}');
				first = false;
				break;
			default:
				elog(ERROR, "unknown flag of jsonb iterator");
		}
	}

	Assert(level == 0);

	return out->data;
}
Beispiel #9
0
static bool
GetNextArgument(const char *ptr, char **arg, Oid *argtype, const char **endptr, const char *path, bool argistype)
{
    const char *p;
    bool		first_arg = false;
    int			len;

    p = ptr;
    while (isspace((unsigned char) *p))
        p++;

    if (*p == '(')
        first_arg = true;
    else if (*p == ',')
        first_arg = false;
    else if (*p == ')')
    {
        p++;
        while (isspace((unsigned char) *p))
            p++;

        if (*p != '\0')
            ereport(ERROR,
                    (errcode(ERRCODE_SYNTAX_ERROR),
                     errmsg("function call syntax error: %s", path)));

        *endptr = p;
        return false;
    }
    else
        ereport(ERROR,
                (errcode(ERRCODE_SYNTAX_ERROR),
                 errmsg("function call syntax error: %s", path)));

    p++;
    while (isspace((unsigned char) *p))
        p++;

    if (first_arg && *p == ')')
    {
        p++;
        while (isspace((unsigned char) *p))
            p++;

        if (*p != '\0')
            ereport(ERROR,
                    (errcode(ERRCODE_SYNTAX_ERROR),
                     errmsg("function call syntax error: %s", path)));

        *endptr = p;
        return false;
    }

    *argtype = UNKNOWNOID;
    if (argistype)
    {
        /* argument is data type name */
        const char *startptr;
        bool		inparenthesis;
        int			nparentheses;
        char	   *str;
        int32		typmod;

        startptr = p;
        inparenthesis = false;
        nparentheses = 0;
        while (*p != '\0' && (inparenthesis || (*p != ',' && *p != ')')))
        {
            if (*p == '(')
            {
                if (inparenthesis)
                    ereport(ERROR,
                            (errcode(ERRCODE_SYNTAX_ERROR),
                             errmsg("function call syntax error: %s", path)));

                inparenthesis = true;
                nparentheses++;
                if (nparentheses > 1)
                    ereport(ERROR,
                            (errcode(ERRCODE_SYNTAX_ERROR),
                             errmsg("function call syntax error: %s", path)));
            }

            if (*p == ')')
                inparenthesis = false;

            p++;
        }

        if (p == startptr)
            ereport(ERROR,
                    (errcode(ERRCODE_SYNTAX_ERROR),
                     errmsg("function call syntax error: %s", path)));

        while (isspace((unsigned char) *(p - 1)))
            p--;

        len = p - startptr;
        str = palloc(len + 1);
        memcpy(str, startptr, len);
        str[len] = '\0';
        *arg = str;

        /* Use full parser to resolve the type name */
        parseTypeString(*arg, argtype, &typmod);
    }
    else if (*p == '\'')
    {
        /* argument is string constants */
        StringInfoData	buf;

        initStringInfo(&buf);

        p++;
        while (*p != '\0')
        {
            if (*p == '\'')
            {
                if (*(p + 1) == '\'')
                    p++;
                else
                    break;
            }

            appendStringInfoCharMacro(&buf, *p++);
        }

        if (*p != '\'')
            ereport(ERROR,
                    (errcode(ERRCODE_SYNTAX_ERROR),
                     errmsg("function call syntax error: %s", path)));

        p++;
        *arg = buf.data;
    }
    else if (pg_strncasecmp(p, "NULL", strlen("NULL")) == 0)
    {
        /* argument is NULL */
        p += strlen("NULL");
        *arg = NULL;
    }
    else
    {
        /* argument is numeric constants */
        bool		minus;
        const char *startptr;
        char	   *str;
        int64		val64;

        /* parse plus operator and minus operator */
        minus = false;
        while (*p == '+' || *p == '-')
        {
            if (*p == '-')
            {
                /* this is standard SQL comment */
                if (*(p + 1) == '-')
                    ereport(ERROR,
                            (errcode(ERRCODE_SYNTAX_ERROR),
                             errmsg("function call syntax error: %s", path)));
                minus = !minus;
            }

            p++;
            while (isspace((unsigned char) *p))
                p++;
        }

        startptr = p;
        while (!isspace((unsigned char) *p) && *p != ',' && *p != ')' &&
                *p != '\0')
            p++;

        len = p - startptr;
        if (len == 0)
            ereport(ERROR,
                    (errcode(ERRCODE_SYNTAX_ERROR),
                     errmsg("function call syntax error: %s", path)));

        str = palloc(len + 2);
        snprintf(str, len + 2, "%c%s", minus ? '-' : '+', startptr);

        /* could be an oversize integer as well as a float ... */
        if (scanint8(str, true, &val64))
        {
            /*
             * It might actually fit in int32. Probably only INT_MIN can
             * occur, but we'll code the test generally just to be sure.
             */
            int32		val32 = (int32) val64;

            if (val64 == (int64) val32)
                *argtype = INT4OID;
            else
                *argtype = INT8OID;
        }
        else
        {
            /* arrange to report location if numeric_in() fails */
            DirectFunctionCall3(numeric_in, CStringGetDatum(str + 1),
                                ObjectIdGetDatum(InvalidOid),
                                Int32GetDatum(-1));

            *argtype = NUMERICOID;
        }

        /* Check for numeric constants. */
        *arg = str;
    }

    *endptr = p;
    return true;
}
Beispiel #10
0
static char *
variant_out_int(FunctionCallInfo fcinfo, Variant input)
{
    VariantCache	*cache;
    bool					need_quote;
    char					*tmp;
    char					*org_cstring;
    StringInfoData	outd;
    StringInfo		out = &outd;
    VariantInt		vi;

    Assert(fcinfo->flinfo->fn_strict); /* Must be strict */

    vi = make_variant_int(input, fcinfo, IOFunc_output);
    cache = GetCache(fcinfo);
    Assert(cache->formatted_name);

    /* Start building string */
    initStringInfo(out);
    appendStringInfoChar(out, '(');

    need_quote = false;
    for (tmp = cache->formatted_name; *tmp; tmp++)
    {
        char		ch = *tmp;

        if (ch == '"' || ch == '\\' ||
                ch == '(' || ch == ')' || ch == ',' ||
                isspace((unsigned char) ch))
        {
            need_quote = true;
            break;
        }
    }
    if(!need_quote)
        appendStringInfoString(out, cache->formatted_name);
    else
    {
        appendStringInfoChar(out, '"');
        for (tmp = cache->formatted_name; *tmp; tmp++)
        {
            char		ch = *tmp;

            if (ch == '"' || ch == '\\')
                appendStringInfoCharMacro(out, ch);
            appendStringInfoCharMacro(out, ch);
        }
        appendStringInfoChar(out, '"');
    }
    appendStringInfoChar(out, ',');

    if(!vi->isnull)
    {
        org_cstring = OutputFunctionCall(&cache->proc, vi->data);

        /*
         * Detect whether we need double quotes for this value
         *
         * Stolen then modified from record_out.
         */
        need_quote = (org_cstring[0] == '\0' ); /* force quotes for empty string */
        if( !need_quote )
        {
            for (tmp = org_cstring; *tmp; tmp++)
            {
                char		ch = *tmp;

                if (ch == '"' || ch == '\\' ||
                        ch == '(' || ch == ')' || ch == ',' ||
                        isspace((unsigned char) ch))
                {
                    need_quote = true;
                    break;
                }
            }
        }

        if (!need_quote)
            appendStringInfoString(out, org_cstring);
        else
        {
            appendStringInfoChar(out, '"');
            for (tmp = org_cstring; *tmp; tmp++)
            {
                char		ch = *tmp;

                if (ch == '"' || ch == '\\')
                    appendStringInfoCharMacro(out, ch);
                appendStringInfoCharMacro(out, ch);
            }
            appendStringInfoChar(out, '"');
        }
    }

    appendStringInfoChar(out, ')');

    return out->data;
}
Beispiel #11
0
/*
 * Output an attribute to text
 * This takes portions of the code of CopyAttributeOutText
 */
static void
attribute_out_text(StringInfo buf, char *string)
{
	char	   *ptr;
	char		c;
	char	   *start;
	char		delimc = COPYOPS_DELIMITER;
	bool		need_transcoding, encoding_embeds_ascii;
	int			file_encoding = pg_get_client_encoding();

	need_transcoding = (file_encoding != GetDatabaseEncoding() ||
						pg_database_encoding_max_length() > 1);
	encoding_embeds_ascii = PG_ENCODING_IS_CLIENT_ONLY(file_encoding);

	if (need_transcoding)
		ptr = pg_server_to_any(string, strlen(string), file_encoding);
	else
		ptr = string;

	/*
	 * We have to grovel through the string searching for control characters
	 * and instances of the delimiter character.  In most cases, though, these
	 * are infrequent.	To avoid overhead from calling CopySendData once per
	 * character, we dump out all characters between escaped characters in a
	 * single call.  The loop invariant is that the data from "start" to "ptr"
	 * can be sent literally, but hasn't yet been.
	 *
	 * We can skip pg_encoding_mblen() overhead when encoding is safe, because
	 * in valid backend encodings, extra bytes of a multibyte character never
	 * look like ASCII.  This loop is sufficiently performance-critical that
	 * it's worth making two copies of it to get the IS_HIGHBIT_SET() test out
	 * of the normal safe-encoding path.
	 */
	if (encoding_embeds_ascii)
	{
		start = ptr;
		while ((c = *ptr) != '\0')
		{
			if ((unsigned char) c < (unsigned char) 0x20)
			{
				/*
				 * \r and \n must be escaped, the others are traditional. We
				 * prefer to dump these using the C-like notation, rather than
				 * a backslash and the literal character, because it makes the
				 * dump file a bit more proof against Microsoftish data
				 * mangling.
				 */
				switch (c)
				{
					case '\b':
						c = 'b';
						break;
					case '\f':
						c = 'f';
						break;
					case '\n':
						c = 'n';
						break;
					case '\r':
						c = 'r';
						break;
					case '\t':
						c = 't';
						break;
					case '\v':
						c = 'v';
						break;
					default:
						/* If it's the delimiter, must backslash it */
						if (c == delimc)
							break;
						/* All ASCII control chars are length 1 */
						ptr++;
						continue;		/* fall to end of loop */
				}

				/* if we get here, we need to convert the control char */
				DUMPSOFAR();
				appendStringInfoCharMacro(buf, '\\');
				appendStringInfoCharMacro(buf, c);
				start = ++ptr;
			}
			else if (c == '\\' || c == delimc)
			{
				DUMPSOFAR();
				appendStringInfoCharMacro(buf, '\\');
				appendStringInfoCharMacro(buf, c);
				start = ++ptr;
			}
			else if (IS_HIGHBIT_SET(c))
				ptr += pg_encoding_mblen(file_encoding, ptr);
			else
				ptr++;
		}
	}
	else
	{
		start = ptr;
		while ((c = *ptr) != '\0')
		{
			if ((unsigned char) c < (unsigned char) 0x20)
			{
				/*
				 * \r and \n must be escaped, the others are traditional. We
				 * prefer to dump these using the C-like notation, rather than
				 * a backslash and the literal character, because it makes the
				 * dump file a bit more proof against Microsoftish data
				 * mangling.
				 */
				switch (c)
				{
					case '\b':
						c = 'b';
						break;
					case '\f':
						c = 'f';
						break;
					case '\n':
						c = 'n';
						break;
					case '\r':
						c = 'r';
						break;
					case '\t':
						c = 't';
						break;
					case '\v':
						c = 'v';
						break;
					default:
						/* If it's the delimiter, must backslash it */
						if (c == delimc)
							break;
						/* All ASCII control chars are length 1 */
						ptr++;
						continue;		/* fall to end of loop */
				}
				/* if we get here, we need to convert the control char */
				DUMPSOFAR();
				appendStringInfoCharMacro(buf, '\\');
				appendStringInfoCharMacro(buf, c);
				start = ++ptr;
			}
			else if (c == '\\' || c == delimc)
			{
				DUMPSOFAR();
				appendStringInfoCharMacro(buf, '\\');
				appendStringInfoCharMacro(buf, c);
				start = ++ptr;
			}
			else
				ptr++;
		}
	}

	DUMPSOFAR();
}
Beispiel #12
0
/*
 * CopyOps_BuildOneRowTo
 * Build one row message to be sent to remote nodes through COPY protocol
 */
char *
CopyOps_BuildOneRowTo(TupleDesc tupdesc, Datum *values, bool *nulls, int *len)
{
	bool		need_delim = false;
	char	   *res;
	int			i;
	FmgrInfo   *out_functions;
	Form_pg_attribute *attr = tupdesc->attrs;
	StringInfo	buf;

	/* Get info about the columns we need to process. */
	out_functions = (FmgrInfo *) palloc(tupdesc->natts * sizeof(FmgrInfo));
	for (i = 0; i < tupdesc->natts; i++)
	{
		Oid			out_func_oid;
		bool		isvarlena;

		/* Do not need any information for dropped attributes */
		if (attr[i]->attisdropped)
			continue;

		getTypeOutputInfo(attr[i]->atttypid,
						  &out_func_oid,
						  &isvarlena);
		fmgr_info(out_func_oid, &out_functions[i]);
	}

	/* Initialize output buffer */
	buf = makeStringInfo();

	for (i = 0; i < tupdesc->natts; i++)
	{
		Datum		value = values[i];
		bool		isnull = nulls[i];

		/* Do not need any information for dropped attributes */
		if (attr[i]->attisdropped)
			continue;

		if (need_delim)
			appendStringInfoCharMacro(buf, COPYOPS_DELIMITER);
		need_delim = true;

		if (isnull)
		{
			/* Null print value to client */
			appendBinaryStringInfo(buf, "\\N", strlen("\\N"));
		}
		else
		{
			char *string;
			string = OutputFunctionCall(&out_functions[i],
										value);
			attribute_out_text(buf, string);
			pfree(string);
		}
	}

	/* Record length of message */
	*len = buf->len;
	res = pstrdup(buf->data);
	pfree(out_functions);
	pfree(buf->data);
	pfree(buf);
	return res;
}
Beispiel #13
0
/*
 * record_out		- output routine for any composite type.
 */
Datum
record_out(PG_FUNCTION_ARGS)
{
	HeapTupleHeader rec = PG_GETARG_HEAPTUPLEHEADER(0);
	Oid			tupType;
	int32		tupTypmod;
	TupleDesc	tupdesc;
	HeapTupleData tuple;
	RecordIOData *my_extra;
	bool		needComma = false;
	int			ncolumns;
	int			i;
	Datum	   *values;
	bool	   *nulls;
	StringInfoData buf;

	/* Extract type info from the tuple itself */
	tupType = HeapTupleHeaderGetTypeId(rec);
	tupTypmod = HeapTupleHeaderGetTypMod(rec);
	tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
	ncolumns = tupdesc->natts;

	/* Build a temporary HeapTuple control structure */
	tuple.t_len = HeapTupleHeaderGetDatumLength(rec);
	ItemPointerSetInvalid(&(tuple.t_self));
	tuple.t_tableOid = InvalidOid;
	tuple.t_data = rec;

	/*
	 * We arrange to look up the needed I/O info just once per series of
	 * calls, assuming the record type doesn't change underneath us.
	 */
	my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
	if (my_extra == NULL ||
		my_extra->ncolumns != ncolumns)
	{
		fcinfo->flinfo->fn_extra =
			MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
							   sizeof(RecordIOData) - sizeof(ColumnIOData)
							   + ncolumns * sizeof(ColumnIOData));
		my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
		my_extra->record_type = InvalidOid;
		my_extra->record_typmod = 0;
	}

	if (my_extra->record_type != tupType ||
		my_extra->record_typmod != tupTypmod)
	{
		MemSet(my_extra, 0,
			   sizeof(RecordIOData) - sizeof(ColumnIOData)
			   + ncolumns * sizeof(ColumnIOData));
		my_extra->record_type = tupType;
		my_extra->record_typmod = tupTypmod;
		my_extra->ncolumns = ncolumns;
	}

	values = (Datum *) palloc(ncolumns * sizeof(Datum));
	nulls = (bool *) palloc(ncolumns * sizeof(bool));

	/* Break down the tuple into fields */
	heap_deform_tuple(&tuple, tupdesc, values, nulls);

	/* And build the result string */
	initStringInfo(&buf);

	appendStringInfoChar(&buf, '(');

	for (i = 0; i < ncolumns; i++)
	{
		ColumnIOData *column_info = &my_extra->columns[i];
		Oid			column_type = tupdesc->attrs[i]->atttypid;
		Datum		attr;
		char	   *value;
		char	   *tmp;
		bool		nq;

		/* Ignore dropped columns in datatype */
		if (tupdesc->attrs[i]->attisdropped)
			continue;

		if (needComma)
			appendStringInfoChar(&buf, ',');
		needComma = true;

		if (nulls[i])
		{
			/* emit nothing... */
			continue;
		}

		/*
		 * Convert the column value to text
		 */
		if (column_info->column_type != column_type)
		{
			getTypeOutputInfo(column_type,
							  &column_info->typiofunc,
							  &column_info->typisvarlena);
			fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
						  fcinfo->flinfo->fn_mcxt);
			column_info->column_type = column_type;
		}

		/*
		 * If we have a toasted datum, forcibly detoast it here to avoid
		 * memory leakage inside the type's output routine.
		 */
		if (column_info->typisvarlena)
			attr = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
		else
			attr = values[i];

		value = OutputFunctionCall(&column_info->proc, attr);

		/* Detect whether we need double quotes for this value */
		nq = (value[0] == '\0');	/* force quotes for empty string */
		for (tmp = value; *tmp; tmp++)
		{
			char		ch = *tmp;

			if (ch == '"' || ch == '\\' ||
				ch == '(' || ch == ')' || ch == ',' ||
				isspace((unsigned char) ch))
			{
				nq = true;
				break;
			}
		}

		/* And emit the string */
		if (nq)
			appendStringInfoCharMacro(&buf, '"');
		for (tmp = value; *tmp; tmp++)
		{
			char		ch = *tmp;

			if (ch == '"' || ch == '\\')
				appendStringInfoCharMacro(&buf, ch);
			appendStringInfoCharMacro(&buf, ch);
		}
		if (nq)
			appendStringInfoCharMacro(&buf, '"');

		pfree(value);

		/* Clean up detoasted copy, if any */
		if (DatumGetPointer(attr) != DatumGetPointer(values[i]))
			pfree(DatumGetPointer(attr));
	}

	appendStringInfoChar(&buf, ')');

	pfree(values);
	pfree(nulls);
	ReleaseTupleDesc(tupdesc);

	PG_RETURN_CSTRING(buf.data);
}
void
tf_write_char(StringInfo msgbuf, char c)
{
	appendStringInfoCharMacro(msgbuf, c);
}
Beispiel #15
0
/*
 * common worker for above two functions
 */
static char *
JsonbToCStringWorker(StringInfo out, JsonbContainer *in, int estimated_len, bool indent)
{
	bool		first = true;
	JsonbIterator *it;
	JsonbValue	v;
	JsonbIteratorToken type = WJB_DONE;
	int			level = 0;
	bool		redo_switch = false;

	/* If we are indenting, don't add a space after a comma */
	int			ispaces = indent ? 1 : 2;

	/*
	 * Don't indent the very first item. This gets set to the indent flag at
	 * the bottom of the loop.
	 */
	bool		use_indent = false;
	bool		raw_scalar = false;
	bool		last_was_key = false;

	if (out == NULL)
		out = makeStringInfo();

	enlargeStringInfo(out, (estimated_len >= 0) ? estimated_len : 64);

	it = JsonbIteratorInit(in);

	while (redo_switch ||
		   ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE))
	{
		redo_switch = false;
		switch (type)
		{
			case WJB_BEGIN_ARRAY:
				if (!first)
					appendBinaryStringInfo(out, ", ", ispaces);

				if (!v.val.array.rawScalar)
				{
					add_indent(out, use_indent && !last_was_key, level);
					appendStringInfoCharMacro(out, '[');
				}
				else
					raw_scalar = true;

				first = true;
				level++;
				break;
			case WJB_BEGIN_OBJECT:
				if (!first)
					appendBinaryStringInfo(out, ", ", ispaces);

				add_indent(out, use_indent && !last_was_key, level);
				appendStringInfoCharMacro(out, '{');

				first = true;
				level++;
				break;
			case WJB_KEY:
				if (!first)
					appendBinaryStringInfo(out, ", ", ispaces);
				first = true;

				add_indent(out, use_indent, level);

				/* json rules guarantee this is a string */
				jsonb_put_escaped_value(out, &v);
				appendBinaryStringInfo(out, ": ", 2);

				type = JsonbIteratorNext(&it, &v, false);
				if (type == WJB_VALUE)
				{
					first = false;
					jsonb_put_escaped_value(out, &v);
				}
				else
				{
					Assert(type == WJB_BEGIN_OBJECT || type == WJB_BEGIN_ARRAY);

					/*
					 * We need to rerun the current switch() since we need to
					 * output the object which we just got from the iterator
					 * before calling the iterator again.
					 */
					redo_switch = true;
				}
				break;
			case WJB_ELEM:
				if (!first)
					appendBinaryStringInfo(out, ", ", ispaces);
				first = false;

				if (!raw_scalar)
					add_indent(out, use_indent, level);
				jsonb_put_escaped_value(out, &v);
				break;
			case WJB_END_ARRAY:
				level--;
				if (!raw_scalar)
				{
					add_indent(out, use_indent, level);
					appendStringInfoCharMacro(out, ']');
				}
				first = false;
				break;
			case WJB_END_OBJECT:
				level--;
				add_indent(out, use_indent, level);
				appendStringInfoCharMacro(out, '}');
				first = false;
				break;
			default:
				elog(ERROR, "unknown jsonb iterator token type");
		}
		use_indent = indent;
		last_was_key = redo_switch;
	}

	Assert(level == 0);

	return out->data;
}
Beispiel #16
0
Datum
triggered_change_notification(PG_FUNCTION_ARGS)
{
	TriggerData *trigdata = (TriggerData *) fcinfo->context;
	Trigger    *trigger;
	int			nargs;
	HeapTuple	trigtuple;
	Relation	rel;
	TupleDesc	tupdesc;
	char	   *channel;
	char		operation;
	StringInfo	payload = makeStringInfo();
	bool		foundPK;

	List	   *indexoidlist;
	ListCell   *indexoidscan;

	/* make sure it's called as a trigger */
	if (!CALLED_AS_TRIGGER(fcinfo))
		ereport(ERROR,
				(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
		errmsg("triggered_change_notification: must be called as trigger")));

	/* and that it's called after the change */
	if (!TRIGGER_FIRED_AFTER(trigdata->tg_event))
		ereport(ERROR,
				(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
				 errmsg("triggered_change_notification: must be called after the change")));

	/* and that it's called for each row */
	if (!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
		ereport(ERROR,
				(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
				 errmsg("triggered_change_notification: must be called for each row")));

	if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
		operation = 'I';
	else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
		operation = 'U';
	else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
		operation = 'D';
	else
	{
		elog(ERROR, "triggered_change_notification: trigger fired by unrecognized operation");
		operation = 'X';		/* silence compiler warning */
	}

	trigger = trigdata->tg_trigger;
	nargs = trigger->tgnargs;
	if (nargs > 1)
		ereport(ERROR,
				(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
				 errmsg("triggered_change_notification: must not be called with more than one parameter")));

	if (nargs == 0)
		channel = "tcn";
	else
		channel = trigger->tgargs[0];

	/* get tuple data */
	trigtuple = trigdata->tg_trigtuple;
	rel = trigdata->tg_relation;
	tupdesc = rel->rd_att;

	foundPK = false;

	/*
	 * Get the list of index OIDs for the table from the relcache, and look up
	 * each one in the pg_index syscache until we find one marked primary key
	 * (hopefully there isn't more than one such).
	 */
	indexoidlist = RelationGetIndexList(rel);

	foreach(indexoidscan, indexoidlist)
	{
		Oid			indexoid = lfirst_oid(indexoidscan);
		HeapTuple	indexTuple;
		Form_pg_index index;

		indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
		if (!HeapTupleIsValid(indexTuple))		/* should not happen */
			elog(ERROR, "cache lookup failed for index %u", indexoid);
		index = (Form_pg_index) GETSTRUCT(indexTuple);
		/* we're only interested if it is the primary key and valid */
		if (index->indisprimary && IndexIsValid(index))
		{
			int			numatts = index->indnatts;

			if (numatts > 0)
			{
				int			i;

				foundPK = true;

				strcpy_quoted(payload, RelationGetRelationName(rel), '"');
				appendStringInfoCharMacro(payload, ',');
				appendStringInfoCharMacro(payload, operation);

				for (i = 0; i < numatts; i++)
				{
					int			colno = index->indkey.values[i];

					appendStringInfoCharMacro(payload, ',');
					strcpy_quoted(payload, NameStr((tupdesc->attrs[colno - 1])->attname), '"');
					appendStringInfoCharMacro(payload, '=');
					strcpy_quoted(payload, SPI_getvalue(trigtuple, tupdesc, colno), '\'');
				}

				Async_Notify(channel, payload->data);
			}
			ReleaseSysCache(indexTuple);
			break;
		}
		ReleaseSysCache(indexTuple);
	}