Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 0 additions & 31 deletions .github/workflows/ci.yml

This file was deleted.

7 changes: 5 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,12 @@ bin/
# Ignore installed modules through `v install --local`:
modules/

# Module resolution symlink (Option B layout)
pg_query

# Compiled C bridge objects
pg_query/*.o
pg_query/*.o.tmp
*.o
*.o.tmp

# Compiled example binaries
examples/bench
Expand Down
17 changes: 8 additions & 9 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
.PHONY: all build test clean
.PHONY: all build test clean dev-setup

all: build

build: pg_query/c_bridge.o libpg_query/libpg_query.a

pg_query/c_bridge.o: pg_query/c_bridge.c pg_query/c_bridge.h libpg_query/pg_query.h
cc -c -I libpg_query pg_query/c_bridge.c -o pg_query/c_bridge.o

libpg_query/libpg_query.a:
build:
make -C libpg_query build
cp libpg_query/libpg_query.a c/libpg_query.a

dev-setup:
ln -sf . pg_query

test: build
v test pg_query/
v test .

clean:
rm -f pg_query/c_bridge.o
make -C libpg_query clean
rm -rf c/libpg_query.a
16 changes: 3 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,8 @@ cd pg_query.v
# Build the static C library (requires Internet — downloads Postgres 17.7 source)
make -C libpg_query build

# Build the C helper object
make build

# Verify everything works
v test pg_query/
v test .
```

## Usage
Expand Down Expand Up @@ -188,11 +185,8 @@ parse('SELECT $$$') or {
# Build C library
make -C libpg_query build

# Build the C helper object
make build

# Run tests
v test pg_query/
v test .

# Run examples
v run examples/parse_sql.v
Expand All @@ -204,22 +198,18 @@ v -o examples/parse_sql examples/parse_sql.v && ./examples/parse_sql
v -o examples/concurrent_parse examples/concurrent_parse.v && ./examples/concurrent_parse
v -o examples/bench examples/bench.v && ./examples/bench

# Rebuild C helper (after editing c_bridge.c)
cc -c -I libpg_query pg_query/c_bridge.c -o pg_query/c_bridge.o

# Regenerate AST structs and decoders (after changing proto schema)
v run tools/gen_ast.v
```

## How it works

The library bundles [libpg_query](https://github.com/pganalyze/libpg_query) (version 6.2.2, wrapping PostgreSQL 17.7), pre-built as a static archive (`libpg_query.a`). The V wrapper in `pg_query/` has these layers:
The library bundles [libpg_query](https://github.com/pganalyze/libpg_query) (version 6.2.2, wrapping PostgreSQL 17.7), pre-built as a static archive (`libpg_query.a`). The V wrapper has these layers:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Static library path looks stale after the C layout move.

This line still references libpg_query.a; with the new structure it should point to c/libpg_query.a to match the build output.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` at line 207, Update the README reference to the static archive
path: replace occurrences of "libpg_query.a" with the new build output path
"c/libpg_query.a" (the string literal "libpg_query.a" in the sentence describing
the bundled library) so the documentation matches the updated C layout and build
artifacts.


| Layer | Files | Role |
|---|---|---|
| **C bindings** | `pgquery.c.v` | `#flag` / `#include` declarations for the C ABI |
| **V wrapper** | `pgquery.v` | Safe `!` result types for all public APIs |
| **C helpers** | `c_bridge.c/h` | Thin C helpers for deparse opts, version strings |
| **Protobuf helpers** | `pg_query_protobuf.v` | V-native protobuf wire-format read/write helpers |
| **Generated decoders** | `pg_query_decode.v` (generated) | 270+ per-message `decode_*` functions |
| **Generated encoders** | `pg_query_encode.v` (generated) | 270+ per-message `encode_*` functions |
Expand Down
Binary file added c/libpg_query.a
Binary file not shown.
177 changes: 177 additions & 0 deletions c/pg_query.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
#ifndef PG_QUERY_H
#define PG_QUERY_H

#include <stdbool.h>
#include <stdint.h>
#include <sys/types.h>

#include "postgres_deparse.h"

Comment on lines +4 to +9
typedef struct {
char* message; // exception message
char* funcname; // source function of exception (e.g. SearchSysCache)
char* filename; // source of exception (e.g. parse.l)
int lineno; // source of exception (e.g. 104)
int cursorpos; // char in query at which exception occurred
char* context; // additional context (optional, can be NULL)
} PgQueryError;

typedef struct {
int length;
bool *items;
PgQueryError* error;
} PgQueryIsUtilityResult;

typedef struct {
size_t len;
char* data;
} PgQueryProtobuf;

typedef struct {
PgQueryProtobuf pbuf;
char* stderr_buffer;
PgQueryError* error;
} PgQueryScanResult;

typedef struct {
char* parse_tree;
char* stderr_buffer;
PgQueryError* error;
} PgQueryParseResult;

typedef struct {
PgQueryProtobuf parse_tree;
char* stderr_buffer;
PgQueryError* error;
} PgQueryProtobufParseResult;

typedef struct {
int stmt_location;
int stmt_len;
} PgQuerySplitStmt;

typedef struct {
PgQuerySplitStmt **stmts;
int n_stmts;
char* stderr_buffer;
PgQueryError* error;
} PgQuerySplitResult;

typedef struct {
char* query;
PgQueryError* error;
} PgQueryDeparseResult;

typedef struct {
PostgresDeparseComment **comments;
size_t comment_count;
PgQueryError* error;
} PgQueryDeparseCommentsResult;

typedef struct {
char* plpgsql_funcs;
PgQueryError* error;
} PgQueryPlpgsqlParseResult;

typedef struct {
uint64_t fingerprint;
char* fingerprint_str;
char* stderr_buffer;
PgQueryError* error;
} PgQueryFingerprintResult;

typedef struct {
char* normalized_query;
PgQueryError* error;
} PgQueryNormalizeResult;

typedef struct {
PgQueryProtobuf summary;
char* stderr_buffer;
PgQueryError* error;
} PgQuerySummaryParseResult;

// Postgres parser options (parse mode and GUCs that affect parsing)

typedef enum
{
PG_QUERY_PARSE_DEFAULT = 0,
PG_QUERY_PARSE_TYPE_NAME,
PG_QUERY_PARSE_PLPGSQL_EXPR,
PG_QUERY_PARSE_PLPGSQL_ASSIGN1,
PG_QUERY_PARSE_PLPGSQL_ASSIGN2,
PG_QUERY_PARSE_PLPGSQL_ASSIGN3
} PgQueryParseMode;

// We technically only need 3 bits to store parse mode, but
// having 4 bits avoids API breaks if another one gets added.
#define PG_QUERY_PARSE_MODE_BITS 4
#define PG_QUERY_PARSE_MODE_BITMASK ((1 << PG_QUERY_PARSE_MODE_BITS) - 1)

#define PG_QUERY_DISABLE_BACKSLASH_QUOTE 16 // backslash_quote = off (default is safe_encoding, which is effectively on)
#define PG_QUERY_DISABLE_STANDARD_CONFORMING_STRINGS 32 // standard_conforming_strings = off (default is on)
#define PG_QUERY_DISABLE_ESCAPE_STRING_WARNING 64 // escape_string_warning = off (default is on)

#ifdef __cplusplus
extern "C" {
#endif

PgQueryNormalizeResult pg_query_normalize(const char* input);
PgQueryNormalizeResult pg_query_normalize_utility(const char* input);
PgQueryScanResult pg_query_scan(const char* input);
PgQueryParseResult pg_query_parse(const char* input);
PgQueryParseResult pg_query_parse_opts(const char* input, int parser_options);
PgQueryProtobufParseResult pg_query_parse_protobuf(const char* input);
PgQueryProtobufParseResult pg_query_parse_protobuf_opts(const char* input, int parser_options);
PgQueryPlpgsqlParseResult pg_query_parse_plpgsql(const char* input);

PgQueryFingerprintResult pg_query_fingerprint(const char* input);
PgQueryFingerprintResult pg_query_fingerprint_opts(const char* input, int parser_options);

// Use pg_query_split_with_scanner when you need to split statements that may
// contain parse errors, otherwise pg_query_split_with_parser is recommended
// for improved accuracy due the parser adding additional token handling.
//
// Note that we try to support special cases like comments, strings containing
// ";" on both, as well as oddities like "CREATE RULE .. (SELECT 1; SELECT 2);"
// which is treated as as single statement.
PgQuerySplitResult pg_query_split_with_scanner(const char *input);
PgQuerySplitResult pg_query_split_with_parser(const char *input);

PgQueryDeparseResult pg_query_deparse_protobuf(PgQueryProtobuf parse_tree);
PgQueryDeparseResult pg_query_deparse_protobuf_opts(PgQueryProtobuf parse_tree, struct PostgresDeparseOpts opts);
PgQueryDeparseCommentsResult pg_query_deparse_comments_for_query(const char *query);

PgQueryIsUtilityResult pg_query_is_utility_stmt(const char *query);

PgQuerySummaryParseResult pg_query_summary(const char* input, int parser_options, int truncate_limit);

void pg_query_free_normalize_result(PgQueryNormalizeResult result);
void pg_query_free_scan_result(PgQueryScanResult result);
void pg_query_free_parse_result(PgQueryParseResult result);
void pg_query_free_split_result(PgQuerySplitResult result);
void pg_query_free_deparse_result(PgQueryDeparseResult result);
void pg_query_free_deparse_comments_result(PgQueryDeparseCommentsResult result);
void pg_query_free_protobuf_parse_result(PgQueryProtobufParseResult result);
void pg_query_free_plpgsql_parse_result(PgQueryPlpgsqlParseResult result);
void pg_query_free_fingerprint_result(PgQueryFingerprintResult result);
void pg_query_free_is_utility_result(PgQueryIsUtilityResult result);
void pg_query_free_summary_parse_result(PgQuerySummaryParseResult result);

// Optional, cleans up the top-level memory context (automatically done for threads that exit)
void pg_query_exit(void);

// Postgres version information
#define PG_MAJORVERSION "17"
#define PG_VERSION "17.7"
#define PG_VERSION_NUM 170007

// Deprecated APIs below

void pg_query_init(void); // Deprecated as of 9.5-1.4.1, this is now run automatically as needed

#ifdef __cplusplus
}
#endif

#endif
34 changes: 34 additions & 0 deletions c/postgres_deparse.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#ifndef POSTGRES_DEPARSE_H
#define POSTGRES_DEPARSE_H

#include <stdbool.h>
#include <sys/types.h>

typedef struct PostgresDeparseComment {
int match_location; // Insert comment before a node, once we find a node whose location field is equal-or-higher than this location
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)
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)
char *str; // The actual comment string, including comment start/end tokens, and newline characters in comment (if any)
} PostgresDeparseComment;

typedef struct PostgresDeparseOpts {
PostgresDeparseComment **comments;
size_t comment_count;

// Pretty print options
bool pretty_print;
int indent_size; // Indentation size (Default 4 spaces)
int max_line_length; // Restricts the line length of certain lists of items (Default 80 characters)
bool trailing_newline; // Whether to add a trailing newline at the end of the output (Default off)
bool commas_start_of_line; // Place separating commas at start of line (Default off)
} PostgresDeparseOpts;

/* Forward declarations to allow referencing the structs in this include file without needing Postgres includes */
struct StringInfoData;
typedef struct StringInfoData *StringInfo;
struct RawStmt;

extern void deparseRawStmt(StringInfo str, struct RawStmt *raw_stmt);
extern void deparseRawStmtOpts(StringInfo str, struct RawStmt *raw_stmt, PostgresDeparseOpts opts);

#endif
Loading