Skip to content

Commit d2a2294

Browse files
committed
Add support for deparser options, update libpg_query
TODO: This is currently pulled from a not-yet-tagged version of libpg_query.
1 parent ff09cde commit d2a2294

24 files changed

+5043
-3915
lines changed

Rakefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ task :update_source do
6666
system("mv #{extdir}/postgres/* #{extdir}/")
6767
system("rmdir #{extdir}/postgres")
6868
system("cp -a #{libdir}/pg_query.h #{extdir}/include")
69+
system("cp -a #{libdir}/postgres_deparse.h #{extdir}/include")
6970
# Protobuf definitions
7071
system("protoc --proto_path=#{libdir}/protobuf --ruby_out=#{File.join(__dir__, 'lib/pg_query')} #{libdir}/protobuf/pg_query.proto")
7172
system("mkdir -p #{extdir}/include/protobuf")

ext/pg_query/COPYRIGHT

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
PostgreSQL Database Management System
2+
(formerly known as Postgres, then as Postgres95)
3+
4+
Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
5+
6+
Portions Copyright (c) 1994, The Regents of the University of California
7+
8+
Permission to use, copy, modify, and distribute this software and its
9+
documentation for any purpose, without fee, and without a written agreement
10+
is hereby granted, provided that the above copyright notice and this
11+
paragraph and the following two paragraphs appear in all copies.
12+
13+
IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
14+
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
15+
LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
16+
DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE
17+
POSSIBILITY OF SUCH DAMAGE.
18+
19+
THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
20+
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
21+
AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
22+
ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO
23+
PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

ext/pg_query/include/pg_query.h

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
#include <stdint.h>
55
#include <sys/types.h>
66

7+
#include "postgres_deparse.h"
8+
79
typedef struct {
810
char* message; // exception message
911
char* funcname; // source function of exception (e.g. SearchSysCache)
@@ -53,6 +55,12 @@ typedef struct {
5355
PgQueryError* error;
5456
} PgQueryDeparseResult;
5557

58+
typedef struct {
59+
PostgresDeparseComment **comments;
60+
size_t comment_count;
61+
PgQueryError* error;
62+
} PgQueryDeparseCommentsResult;
63+
5664
typedef struct {
5765
char* plpgsql_funcs;
5866
PgQueryError* error;
@@ -118,12 +126,15 @@ PgQuerySplitResult pg_query_split_with_scanner(const char *input);
118126
PgQuerySplitResult pg_query_split_with_parser(const char *input);
119127

120128
PgQueryDeparseResult pg_query_deparse_protobuf(PgQueryProtobuf parse_tree);
129+
PgQueryDeparseResult pg_query_deparse_protobuf_opts(PgQueryProtobuf parse_tree, struct PostgresDeparseOpts opts);
130+
PgQueryDeparseCommentsResult pg_query_deparse_comments_for_query(const char *query);
121131

122132
void pg_query_free_normalize_result(PgQueryNormalizeResult result);
123133
void pg_query_free_scan_result(PgQueryScanResult result);
124134
void pg_query_free_parse_result(PgQueryParseResult result);
125135
void pg_query_free_split_result(PgQuerySplitResult result);
126136
void pg_query_free_deparse_result(PgQueryDeparseResult result);
137+
void pg_query_free_deparse_comments_result(PgQueryDeparseCommentsResult result);
127138
void pg_query_free_protobuf_parse_result(PgQueryProtobufParseResult result);
128139
void pg_query_free_plpgsql_parse_result(PgQueryPlpgsqlParseResult result);
129140
void pg_query_free_fingerprint_result(PgQueryFingerprintResult result);
@@ -133,8 +144,8 @@ void pg_query_exit(void);
133144

134145
// Postgres version information
135146
#define PG_MAJORVERSION "17"
136-
#define PG_VERSION "17.4"
137-
#define PG_VERSION_NUM 170004
147+
#define PG_VERSION "17.5"
148+
#define PG_VERSION_NUM 170005
138149

139150
// Deprecated APIs below
140151

ext/pg_query/include/postgres/mb/pg_wchar.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,8 @@ extern int pg_valid_server_encoding_id(int encoding);
664664
*/
665665
extern void pg_encoding_set_invalid(int encoding, char *dst);
666666
extern int pg_encoding_mblen(int encoding, const char *mbstr);
667+
extern int pg_encoding_mblen_or_incomplete(int encoding, const char *mbstr,
668+
size_t remaining);
667669
extern int pg_encoding_mblen_bounded(int encoding, const char *mbstr);
668670
extern int pg_encoding_dsplen(int encoding, const char *mbstr);
669671
extern int pg_encoding_verifymbchar(int encoding, const char *mbstr, int len);

ext/pg_query/include/postgres/nodes/pathnodes.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1101,8 +1101,7 @@ typedef struct IndexOptInfo IndexOptInfo;
11011101
#define HAVE_INDEXOPTINFO_TYPEDEF 1
11021102
#endif
11031103

1104-
struct IndexPath; /* avoid including pathnodes.h here */
1105-
struct PlannerInfo; /* avoid including pathnodes.h here */
1104+
struct IndexPath; /* forward declaration */
11061105

11071106
struct IndexOptInfo
11081107
{

ext/pg_query/include/postgres/pg_config.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -592,7 +592,7 @@
592592
#define PACKAGE_NAME "PostgreSQL"
593593

594594
/* Define to the full name and version of this package. */
595-
#define PACKAGE_STRING "PostgreSQL 17.4"
595+
#define PACKAGE_STRING "PostgreSQL 17.5"
596596

597597
/* Define to the one symbol short name of this package. */
598598
#define PACKAGE_TARNAME "postgresql"
@@ -601,7 +601,7 @@
601601
#define PACKAGE_URL "https://www.postgresql.org/"
602602

603603
/* Define to the version of this package. */
604-
#define PACKAGE_VERSION "17.4"
604+
#define PACKAGE_VERSION "17.5"
605605

606606
/* Define to the name of a signed 128-bit integer type. */
607607
#define PG_INT128_TYPE __int128
@@ -620,7 +620,7 @@
620620
#define PG_MAJORVERSION_NUM 17
621621

622622
/* PostgreSQL minor version number */
623-
#define PG_MINORVERSION_NUM 4
623+
#define PG_MINORVERSION_NUM 5
624624

625625
/* Define to best printf format archetype, usually gnu_printf if available. */
626626
#define PG_PRINTF_ATTRIBUTE printf
@@ -629,13 +629,13 @@
629629
#define PG_USE_STDBOOL 1
630630

631631
/* PostgreSQL version as a string */
632-
#define PG_VERSION "17.4"
632+
#define PG_VERSION "17.5"
633633

634634
/* PostgreSQL version as a number */
635-
#define PG_VERSION_NUM 170004
635+
#define PG_VERSION_NUM 170005
636636

637637
/* A string containing the version number, platform, and C compiler */
638-
#define PG_VERSION_STR "PostgreSQL 17.4 (libpg_query)"
638+
#define PG_VERSION_STR "PostgreSQL 17.5 (libpg_query)"
639639

640640
/* Define to 1 to allow profiling output to be saved separately for each
641641
process. */

ext/pg_query/include/postgres/pg_config_manual.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@
2626
*
2727
* Changing this requires an initdb.
2828
*/
29+
#ifndef NAMEDATALEN
2930
#define NAMEDATALEN 64
31+
#endif
3032

3133
/*
3234
* Maximum number of arguments to a function.

ext/pg_query/include/postgres/replication/reorderbuffer.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,10 @@ extern TransactionId *ReorderBufferGetCatalogChangesXacts(ReorderBuffer *rb);
729729

730730
extern void ReorderBufferSetRestartPoint(ReorderBuffer *rb, XLogRecPtr ptr);
731731

732+
extern uint32 ReorderBufferGetInvalidations(ReorderBuffer *rb,
733+
TransactionId xid,
734+
SharedInvalidationMessage **msgs);
735+
732736
extern void StartupReorderBuffer(void);
733737

734738
#endif
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#ifndef POSTGRES_DEPARSE_H
2+
#define POSTGRES_DEPARSE_H
3+
4+
#include <stdbool.h>
5+
#include <sys/types.h>
6+
7+
typedef struct PostgresDeparseComment {
8+
int match_location; // Insert comment before a node, once we find a node whose location field is equal-or-higher than this location
9+
int newlines_before_comment; // Insert newlines before inserting the comment (set to non-zero if the source comment was separated from the prior token by at least one newline)
10+
int newlines_after_comment; // Insert newlines after inserting the comment (set to non-zero if the source comment was separated from the next token by at least one newline)
11+
char *str; // The actual comment string, including comment start/end tokens, and newline characters in comment (if any)
12+
} PostgresDeparseComment;
13+
14+
typedef struct PostgresDeparseOpts {
15+
PostgresDeparseComment **comments;
16+
size_t comment_count;
17+
18+
// Pretty print options
19+
bool pretty_print;
20+
int indent_size; // Indentation size (Default 4 spaces)
21+
int max_line_length; // Restricts the line length of certain lists of items (Default 80 characters)
22+
bool trailing_newline; // Whether to add a trailing newline at the end of the output (Default off)
23+
bool commas_start_of_line; // Place separating commas at start of line (Default off)
24+
} PostgresDeparseOpts;
25+
26+
/* Forward declarations to allow referencing the structs in this include file without needing Postgres includes */
27+
struct StringInfoData;
28+
typedef struct StringInfoData *StringInfo;
29+
struct RawStmt;
30+
31+
extern void deparseRawStmt(StringInfo str, struct RawStmt *raw_stmt);
32+
extern void deparseRawStmtOpts(StringInfo str, struct RawStmt *raw_stmt, PostgresDeparseOpts opts);
33+
34+
#endif

ext/pg_query/pg_query_deparse.c

Lines changed: 114 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,20 @@
44

55
#include "postgres_deparse.h"
66

7+
#include "postgres.h"
8+
#include "lib/stringinfo.h"
9+
#include "nodes/parsenodes.h"
10+
11+
#include "protobuf/pg_query.pb-c.h"
12+
713
PgQueryDeparseResult pg_query_deparse_protobuf(PgQueryProtobuf parse_tree)
14+
{
15+
PostgresDeparseOpts opts;
16+
MemSet(&opts, 0, sizeof(PostgresDeparseOpts));
17+
return pg_query_deparse_protobuf_opts(parse_tree, opts);
18+
}
19+
20+
PgQueryDeparseResult pg_query_deparse_protobuf_opts(PgQueryProtobuf parse_tree, PostgresDeparseOpts opts)
821
{
922
PgQueryDeparseResult result = {0};
1023
StringInfoData str;
@@ -21,7 +34,7 @@ PgQueryDeparseResult pg_query_deparse_protobuf(PgQueryProtobuf parse_tree)
2134
initStringInfo(&str);
2235

2336
foreach(lc, stmts) {
24-
deparseRawStmt(&str, castNode(RawStmt, lfirst(lc)));
37+
deparseRawStmtOpts(&str, castNode(RawStmt, lfirst(lc)), opts);
2538
if (lnext(stmts, lc))
2639
appendStringInfoString(&str, "; ");
2740
}
@@ -62,3 +75,103 @@ void pg_query_free_deparse_result(PgQueryDeparseResult result)
6275

6376
free(result.query);
6477
}
78+
79+
PgQueryDeparseCommentsResult pg_query_deparse_comments_for_query(const char *query)
80+
{
81+
PgQueryDeparseCommentsResult result = {0};
82+
PgQueryScanResult scan_result_raw = pg_query_scan(query);
83+
if (scan_result_raw.error)
84+
{
85+
result.error = scan_result_raw.error;
86+
return result;
87+
}
88+
89+
PgQuery__ScanResult *scan_result = pg_query__scan_result__unpack(NULL, scan_result_raw.pbuf.len, (void*) scan_result_raw.pbuf.data);
90+
bool prior_token_was_comment = false;
91+
int32_t prior_non_comment_end = 0;
92+
int32_t prior_token_end = 0;
93+
94+
result.comment_count = 0;
95+
for (int i = 0; i < scan_result->n_tokens; i++)
96+
{
97+
PgQuery__ScanToken *token = scan_result->tokens[i];
98+
if (token->token == PG_QUERY__TOKEN__SQL_COMMENT || token->token == PG_QUERY__TOKEN__C_COMMENT)
99+
result.comment_count++;
100+
}
101+
102+
result.comments = malloc(result.comment_count * sizeof(PostgresDeparseComment*));
103+
size_t comment_idx = 0;
104+
for (int i = 0; i < scan_result->n_tokens; i++)
105+
{
106+
PgQuery__ScanToken *token = scan_result->tokens[i];
107+
if (token->token == PG_QUERY__TOKEN__SQL_COMMENT || token->token == PG_QUERY__TOKEN__C_COMMENT)
108+
{
109+
size_t token_len = token->end - token->start;
110+
PostgresDeparseComment* comment = malloc(sizeof(PostgresDeparseComment));
111+
112+
/*
113+
* Set to the end of the prior non-comment token.
114+
*
115+
* We could alternatively set the match location to the start of the next
116+
* non-comment token instead (which would be more in line with emitting
117+
* that happens before the node once the location is matched).
118+
*
119+
* However, that doesn't work well in practice, specifically. RawStmt start
120+
* locations are 0, even when there are comments at the start of the statement.
121+
*/
122+
comment->match_location = prior_non_comment_end;
123+
124+
comment->newlines_before_comment = 0;
125+
comment->newlines_after_comment = 0;
126+
127+
// Emit newlines before the comment if prior token was not a comment (otherwise that comment would have emitted them)
128+
if (!prior_token_was_comment)
129+
{
130+
for (int j = prior_token_end; j < token->start; j++)
131+
{
132+
if (query[j] == '\n')
133+
comment->newlines_before_comment++;
134+
}
135+
}
136+
137+
if (i < scan_result->n_tokens - 1)
138+
{
139+
for (int j = token->end; j < scan_result->tokens[i + 1]->start; j++)
140+
{
141+
if (query[j] == '\n')
142+
comment->newlines_after_comment++;
143+
}
144+
}
145+
146+
comment->str = malloc(token_len + 1);
147+
memcpy(comment->str, &(query[token->start]), token_len);
148+
comment->str[token_len] = '\0';
149+
150+
result.comments[comment_idx] = comment;
151+
comment_idx++;
152+
prior_token_was_comment = true;
153+
}
154+
else
155+
{
156+
prior_non_comment_end = token->end;
157+
prior_token_was_comment = false;
158+
}
159+
prior_token_end = token->end;
160+
}
161+
162+
pg_query__scan_result__free_unpacked(scan_result, NULL);
163+
pg_query_free_scan_result(scan_result_raw);
164+
165+
return result;
166+
}
167+
168+
void pg_query_free_deparse_comments_result(PgQueryDeparseCommentsResult result)
169+
{
170+
for (int i = 0; i < result.comment_count; i++)
171+
{
172+
free(result.comments[i]->str);
173+
free(result.comments[i]);
174+
}
175+
if (result.comments != NULL)
176+
free(result.comments);
177+
}

0 commit comments

Comments
 (0)