Repository: sqlc-dev/sqlc
Pull request #3811 (2025-01-29 18:05:53)

Update postgresql_type.go: accept "timestamp" as a case
the only type accepted as a case for postgres type `timestamp` is `pg_catalog.timestamp`. It would be better if `timestamp` was also accepted directly, just as `timestamptz` is.


Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 8aec33c8e..34d625bf7 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -38,6 +38,9 @@ jobs:
     - name: install sqlc-gen-test
       run: go install github.com/sqlc-dev/sqlc-gen-test@v0.1.0
 
+    - name: install test-json-process-plugin
+      run: go install ./scripts/test-json-process-plugin/
+
     - name: install ./...
       run: go install ./...
       env:
@@ -75,9 +78,4 @@ jobs:
     timeout-minutes: 5
 
     steps:
-    - uses: actions/checkout@v4
-    - uses: actions/setup-go@v5
-      with:
-        go-version: '1.23.5'
-    - run: go install golang.org/x/vuln/cmd/govulncheck@latest
-    - run: govulncheck ./...
+    - uses: golang/govulncheck-action@v1
diff --git a/Dockerfile b/Dockerfile
index 2e8e74473..9627f2b10 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,5 +1,5 @@
 # STEP 1: Build sqlc
-FROM golang:1.23.5 AS builder
+FROM golang:1.23.6 AS builder
 
 COPY . /workspace
 WORKDIR /workspace
diff --git a/Makefile b/Makefile
index e9c2fbb89..b8745e57d 100644
--- a/Makefile
+++ b/Makefile
@@ -32,6 +32,9 @@ sqlc-pg-gen:
 sqlc-gen-json:
 	go build -o ~/bin/sqlc-gen-json ./cmd/sqlc-gen-json
 
+test-json-process-plugin:
+	go build -o ~/bin/test-json-process-plugin ./scripts/test-json-process-plugin/
+
 start:
 	docker compose up -d
 
diff --git a/docs/guides/plugins.md b/docs/guides/plugins.md
index 0a97a2b77..b229f9173 100644
--- a/docs/guides/plugins.md
+++ b/docs/guides/plugins.md
@@ -72,6 +72,8 @@ For a complete working example see the following files:
   - A process-based plugin that serializes the CodeGenRequest to JSON
 - [process_plugin_sqlc_gen_json](https://github.com/sqlc-dev/sqlc/tree/main/internal/endtoend/testdata/process_plugin_sqlc_gen_json)
   - An example project showing how to use a process-based plugin
+- [process_plugin_sqlc_gen_json](https://github.com/sqlc-dev/sqlc/tree/main/internal/endtoend/testdata/process_plugin_format_json/)
+  - An example project showing how to use a process-based plugin using json
 
 ## Environment variables
 
@@ -99,4 +101,4 @@ plugins:
 ```
 
 A variable named `SQLC_VERSION` is always included in the plugin's
-environment, set to the version of the `sqlc` executable invoking it.
\ No newline at end of file
+environment, set to the version of the `sqlc` executable invoking it.
diff --git a/docs/reference/config.md b/docs/reference/config.md
index 9d334834f..e6e690b6a 100644
--- a/docs/reference/config.md
+++ b/docs/reference/config.md
@@ -273,6 +273,8 @@ Each mapping in the `plugins` collection has the following keys:
 - `process`: A mapping with a single `cmd` key
   - `cmd`:
     - The executable to call when using this plugin
+  - `format`:
+    - The format expected. Supports `json` and `protobuf` formats. Defaults to `protobuf`.
 - `wasm`: A mapping with a two keys `url` and `sha256`
   - `url`:
     - The URL to fetch the WASM file. Supports the `https://` or `file://` schemes.
diff --git a/docs/reference/datatypes.md b/docs/reference/datatypes.md
index a440be657..bb3d8168b 100644
--- a/docs/reference/datatypes.md
+++ b/docs/reference/datatypes.md
@@ -235,7 +235,7 @@ create table shapes(
 );
 
 -- name: GetCentroids :many
-SELECT id, name, ST_Centriod(geom)::geometry FROM shapes;
+SELECT id, name, ST_Centroid(geom)::geometry FROM shapes;
 ```
 
 ```json
diff --git a/docs/reference/language-support.rst b/docs/reference/language-support.rst
index ccb62dc66..c5e8435e6 100644
--- a/docs/reference/language-support.rst
+++ b/docs/reference/language-support.rst
@@ -16,14 +16,17 @@ Community language support
 
 New languages can be added via :doc:`plugins <../guides/plugins>`.
 
-========  =================================  ===============  ============  ===============
-Language  Plugin                             MySQL            PostgreSQL    SQLite
-========  =================================  ===============  ============  ===============
-F#        `kaashyapan/sqlc-gen-fsharp`_      Not implemented  Beta          Beta
-C#        `DaredevilOSS/sqlc-gen-csharp`_    Beta             Beta          Beta
-Ruby      `DaredevilOSS/sqlc-gen-ruby`_      Beta             Beta          Beta
-[Any]     `fdietze/sqlc-gen-from-template`_  Stable           Stable        Stable
-========  =================================  ===============  ============  ===============
+========  =================================  ===============  ===============  ===============
+Language  Plugin                             MySQL            PostgreSQL       SQLite
+========  =================================  ===============  ===============  ===============
+C#        `DaredevilOSS/sqlc-gen-csharp`_    Beta             Beta             Beta
+F#        `kaashyapan/sqlc-gen-fsharp`_      N/A              Beta             Beta
+Java      `tandemdude/sqlc-gen-java`_        N/A              Beta             N/A 
+PHP       `lcarilla/sqlc-plugin-php-dbal`_   Beta             N/A              N/A    
+Ruby      `DaredevilOSS/sqlc-gen-ruby`_      Beta             Beta             Beta           
+Zig       `tinyzimmer/sqlc-gen-zig`_         N/A              Beta             N/A            
+[Any]     `fdietze/sqlc-gen-from-template`_  Stable           Stable           Stable
+========  =================================  ===============  ===============  ===============
 
 .. _sqlc-gen-go: https://github.com/sqlc-dev/sqlc-gen-go
 .. _kaashyapan/sqlc-gen-fsharp: https://github.com/kaashyapan/sqlc-gen-fsharp
@@ -33,6 +36,9 @@ Ruby      `DaredevilOSS/sqlc-gen-ruby`_      Beta             Beta          Beta
 .. _DaredevilOSS/sqlc-gen-csharp: https://github.com/DaredevilOSS/sqlc-gen-csharp
 .. _DaredevilOSS/sqlc-gen-ruby: https://github.com/DaredevilOSS/sqlc-gen-ruby
 .. _fdietze/sqlc-gen-from-template: https://github.com/fdietze/sqlc-gen-from-template
+.. _lcarilla/sqlc-plugin-php-dbal: https://github.com/lcarilla/sqlc-plugin-php-dbal
+.. _tandemdude/sqlc-gen-java: https://github.com/tandemdude/sqlc-gen-java
+.. _tinyzimmer/sqlc-gen-zig: https://github.com/tinyzimmer/sqlc-gen-zig
 
 Future language support
 ************************
diff --git a/docs/requirements.txt b/docs/requirements.txt
index 3a279bc71..135b70826 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -1,9 +1,9 @@
-Babel==2.16.0
+Babel==2.17.0
 Jinja2==3.1.5
 MarkupSafe==3.0.2
 Pygments==2.19.1
 Sphinx==7.4.7
-certifi==2024.12.14
+certifi==2025.1.31
 chardet==5.2.0
 commonmark==0.9.1
 docutils==0.20.1
@@ -12,7 +12,7 @@ imagesize==1.4.1
 myst-parser==4.0.0 
 packaging==24.2
 pyparsing==3.2.1
-pytz==2024.2
+pytz==2025.1
 requests==2.32.3
 snowballstemmer==2.2.0
 sphinx-favicon==1.0.1
diff --git a/go.mod b/go.mod
index 1db724a44..dcfeb016c 100644
--- a/go.mod
+++ b/go.mod
@@ -8,7 +8,7 @@ require (
 	github.com/davecgh/go-spew v1.1.1
 	github.com/fatih/structtag v1.2.0
 	github.com/go-sql-driver/mysql v1.8.1
-	github.com/google/cel-go v0.22.1
+	github.com/google/cel-go v0.23.2
 	github.com/google/go-cmp v0.6.0
 	github.com/jackc/pgx/v4 v4.18.3
 	github.com/jackc/pgx/v5 v5.7.2
@@ -18,19 +18,19 @@ require (
 	github.com/pingcap/tidb/pkg/parser v0.0.0-20241203170126-9812d85d0d25
 	github.com/riza-io/grpc-go v0.2.0
 	github.com/spf13/cobra v1.8.1
-	github.com/spf13/pflag v1.0.5
+	github.com/spf13/pflag v1.0.6
 	github.com/tetratelabs/wazero v1.8.2
 	github.com/wasilibs/go-pgquery v0.0.0-20240606042535-c0843d6592cc
 	github.com/xeipuuv/gojsonschema v1.2.0
-	golang.org/x/sync v0.10.0
-	google.golang.org/grpc v1.69.4
-	google.golang.org/protobuf v1.36.3
+	golang.org/x/sync v0.11.0
+	google.golang.org/grpc v1.70.0
+	google.golang.org/protobuf v1.36.5
 	gopkg.in/yaml.v3 v3.0.1
 	modernc.org/sqlite v1.34.5
 )
 
 require (
-	cel.dev/expr v0.18.0 // indirect
+	cel.dev/expr v0.19.1 // indirect
 	filippo.io/edwards25519 v1.1.0 // indirect
 	github.com/dustin/go-humanize v1.0.1 // indirect
 	github.com/google/uuid v1.6.0 // indirect
@@ -62,8 +62,8 @@ require (
 	golang.org/x/net v0.33.0 // indirect
 	golang.org/x/sys v0.28.0 // indirect
 	golang.org/x/text v0.21.0 // indirect
-	google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53 // indirect
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20241202173237-19429a94021a // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a // indirect
 	gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
 	modernc.org/libc v1.55.3 // indirect
 	modernc.org/mathutil v1.6.0 // indirect
diff --git a/go.sum b/go.sum
index e07f019a2..d8b2f4e8a 100644
--- a/go.sum
+++ b/go.sum
@@ -1,5 +1,5 @@
-cel.dev/expr v0.18.0 h1:CJ6drgk+Hf96lkLikr4rFf19WrU0BOWEihyZnI2TAzo=
-cel.dev/expr v0.18.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
+cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4=
+cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
 filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
 filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
@@ -36,8 +36,8 @@ github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRx
 github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
 github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
 github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
-github.com/google/cel-go v0.22.1 h1:AfVXx3chM2qwoSbM7Da8g8hX8OVSkBFwX+rz2+PcK40=
-github.com/google/cel-go v0.22.1/go.mod h1:BuznPXXfQDpXKWQ9sPW3TzlAJN5zzFe+i9tIs0yC4s8=
+github.com/google/cel-go v0.23.2 h1:UdEe3CvQh3Nv+E/j9r1Y//WO0K0cSyD7/y0bzyLIMI4=
+github.com/google/cel-go v0.23.2/go.mod h1:52Pb6QsDbC5kvgxvZhiL9QX1oZEkcUF/ZqaPx1J5Wwo=
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
 github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
@@ -162,8 +162,9 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB
 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
 github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
-github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
+github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU=
 github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -189,16 +190,16 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:
 github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
 github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
 github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
-go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY=
-go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE=
-go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE=
-go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY=
-go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk=
-go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0=
-go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc=
-go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8=
-go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys=
-go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A=
+go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
+go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
+go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M=
+go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8=
+go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4=
+go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU=
+go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
+go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
+go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM=
+go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8=
 go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
@@ -250,8 +251,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
 golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
 golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
-golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
+golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -293,16 +294,16 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53 h1:fVoAXEKA4+yufmbdVYv+SE73+cPZbbbe8paLsHfkK+U=
-google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53/go.mod h1:riSXTwQ4+nqmPGtobMFyW5FqVAmIs0St6VPp4Ug7CE4=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 h1:X58yt85/IXCx0Y3ZwN6sEIKZzQtDEYaBWrDvErdXrRE=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
-google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A=
-google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
+google.golang.org/genproto/googleapis/api v0.0.0-20241202173237-19429a94021a h1:OAiGFfOiA0v9MRYsSidp3ubZaBnteRUyn3xB2ZQ5G/E=
+google.golang.org/genproto/googleapis/api v0.0.0-20241202173237-19429a94021a/go.mod h1:jehYqy3+AhJU9ve55aNOaSml7wUXjF9x6z2LcCfpAhY=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a h1:hgh8P4EuoxpsuKMXX/To36nOFD7vixReXgn8lPGnt+o=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU=
+google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
+google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU=
-google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
+google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
+google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
diff --git a/internal/cmd/generate.go b/internal/cmd/generate.go
index a7e64e1e4..00e8871c7 100644
--- a/internal/cmd/generate.go
+++ b/internal/cmd/generate.go
@@ -349,8 +349,9 @@ func codegen(ctx context.Context, combo config.CombinedSettings, sql OutputPair,
 		switch {
 		case plug.Process != nil:
 			handler = &process.Runner{
-				Cmd: plug.Process.Cmd,
-				Env: plug.Env,
+				Cmd:    plug.Process.Cmd,
+				Env:    plug.Env,
+				Format: plug.Process.Format,
 			}
 		case plug.WASM != nil:
 			handler = &wasm.Runner{
diff --git a/internal/codegen/golang/postgresql_type.go b/internal/codegen/golang/postgresql_type.go
index 563cc09ab..4518e1aba 100644
--- a/internal/codegen/golang/postgresql_type.go
+++ b/internal/codegen/golang/postgresql_type.go
@@ -233,7 +233,7 @@ func postgresType(req *plugin.GenerateRequest, options *opts.Options, col *plugi
 		}
 		return "sql.NullTime"
 
-	case "pg_catalog.timestamp":
+	case "pg_catalog.timestamp", "timestamp":
 		if driver == opts.SQLDriverPGXV5 {
 			return "pgtype.Timestamp"
 		}
diff --git a/internal/config/config.go b/internal/config/config.go
index 5bfa506b0..0ff805fcc 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -89,7 +89,8 @@ type Plugin struct {
 	Name    string   `json:"name" yaml:"name"`
 	Env     []string `json:"env" yaml:"env"`
 	Process *struct {
-		Cmd string `json:"cmd" yaml:"cmd"`
+		Cmd    string `json:"cmd" yaml:"cmd"`
+		Format string `json:"format" yaml:"format"`
 	} `json:"process" yaml:"process"`
 	WASM *struct {
 		URL    string `json:"url" yaml:"url"`
diff --git a/internal/ext/process/gen.go b/internal/ext/process/gen.go
index b677f6805..b5720dbc3 100644
--- a/internal/ext/process/gen.go
+++ b/internal/ext/process/gen.go
@@ -11,6 +11,7 @@ import (
 	"google.golang.org/grpc"
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/status"
+	"google.golang.org/protobuf/encoding/protojson"
 	"google.golang.org/protobuf/proto"
 	"google.golang.org/protobuf/reflect/protoreflect"
 
@@ -18,8 +19,9 @@ import (
 )
 
 type Runner struct {
-	Cmd string
-	Env []string
+	Cmd    string
+	Format string
+	Env    []string
 }
 
 func (r *Runner) Invoke(ctx context.Context, method string, args any, reply any, opts ...grpc.CallOption) error {
@@ -28,9 +30,27 @@ func (r *Runner) Invoke(ctx context.Context, method string, args any, reply any,
 		return fmt.Errorf("args isn't a protoreflect.ProtoMessage")
 	}
 
-	stdin, err := proto.Marshal(req)
-	if err != nil {
-		return fmt.Errorf("failed to encode codegen request: %w", err)
+	var stdin []byte
+	var err error
+	switch r.Format {
+	case "json":
+		m := &protojson.MarshalOptions{
+			EmitUnpopulated: true,
+			Indent:          "",
+			UseProtoNames:   true,
+		}
+		stdin, err = m.Marshal(req)
+
+		if err != nil {
+			return fmt.Errorf("failed to encode codegen request: %w", err)
+		}
+	case "", "protobuf":
+		stdin, err = proto.Marshal(req)
+		if err != nil {
+			return fmt.Errorf("failed to encode codegen request: %w", err)
+		}
+	default:
+		return fmt.Errorf("unknown plugin format: %s", r.Format)
 	}
 
 	// Check if the output plugin exists
@@ -66,8 +86,15 @@ func (r *Runner) Invoke(ctx context.Context, method string, args any, reply any,
 		return fmt.Errorf("reply isn't a protoreflect.ProtoMessage")
 	}
 
-	if err := proto.Unmarshal(out, resp); err != nil {
-		return fmt.Errorf("process: failed to read codegen resp: %w", err)
+	switch r.Format {
+	case "json":
+		if err := protojson.Unmarshal(out, resp); err != nil {
+			return fmt.Errorf("process: failed to read codegen resp: %w", err)
+		}
+	default:
+		if err := proto.Unmarshal(out, resp); err != nil {
+			return fmt.Errorf("process: failed to read codegen resp: %w", err)
+		}
 	}
 
 	return nil


Test patch:
diff --git a/examples/authors/sqlite/db_test.go b/examples/authors/sqlite/db_test.go
index 84f193959..e10ce4606 100644
--- a/examples/authors/sqlite/db_test.go
+++ b/examples/authors/sqlite/db_test.go
@@ -12,6 +12,7 @@ import (
 
 func TestAuthors(t *testing.T) {
 	sdb, cleanup := sqltest.SQLite(t, []string{"schema.sql"})
+	defer sdb.Close()
 	defer cleanup()
 
 	ctx := context.Background()
diff --git a/examples/booktest/sqlite/db_test.go b/examples/booktest/sqlite/db_test.go
index b9490245b..42a8b396b 100644
--- a/examples/booktest/sqlite/db_test.go
+++ b/examples/booktest/sqlite/db_test.go
@@ -18,6 +18,7 @@ const (
 
 func TestBooks(t *testing.T) {
 	db, cleanup := sqltest.SQLite(t, []string{"schema.sql"})
+	defer db.Close()
 	defer cleanup()
 
 	ctx := context.Background()
diff --git a/examples/ondeck/sqlite/db_test.go b/examples/ondeck/sqlite/db_test.go
index 39264c26d..e4bb4e419 100644
--- a/examples/ondeck/sqlite/db_test.go
+++ b/examples/ondeck/sqlite/db_test.go
@@ -149,6 +149,7 @@ func TestPrepared(t *testing.T) {
 	t.Parallel()
 
 	sdb, cleanup := sqltest.SQLite(t, []string{"schema"})
+	defer sdb.Close()
 	defer cleanup()
 
 	q, err := Prepare(context.Background(), sdb)
@@ -163,6 +164,7 @@ func TestQueries(t *testing.T) {
 	t.Parallel()
 
 	sdb, cleanup := sqltest.SQLite(t, []string{"schema"})
+	defer sdb.Close()
 	defer cleanup()
 
 	runOnDeckQueries(t, New(sdb))
diff --git a/internal/endtoend/testdata/process_plugin_format_json/exec.json b/internal/endtoend/testdata/process_plugin_format_json/exec.json
new file mode 100644
index 000000000..2cfb9f266
--- /dev/null
+++ b/internal/endtoend/testdata/process_plugin_format_json/exec.json
@@ -0,0 +1,4 @@
+{
+  "process": "test-json-process-plugin",
+  "os": [ "darwin", "linux" ]
+}
diff --git a/internal/endtoend/testdata/process_plugin_format_json/gen/hello.txt b/internal/endtoend/testdata/process_plugin_format_json/gen/hello.txt
new file mode 100644
index 000000000..05d6799f1
--- /dev/null
+++ b/internal/endtoend/testdata/process_plugin_format_json/gen/hello.txt
@@ -0,0 +1,12 @@
+SELECT id, name, bio FROM authors
+WHERE id = $1 LIMIT 1
+SELECT id, name, bio FROM authors
+ORDER BY name
+INSERT INTO authors (
+          name, bio
+) VALUES (
+  $1, $2
+)
+RETURNING id, name, bio
+DELETE FROM authors
+WHERE id = $1
diff --git a/internal/endtoend/testdata/process_plugin_format_json/query.sql b/internal/endtoend/testdata/process_plugin_format_json/query.sql
new file mode 100644
index 000000000..75e38b2ca
--- /dev/null
+++ b/internal/endtoend/testdata/process_plugin_format_json/query.sql
@@ -0,0 +1,19 @@
+-- name: GetAuthor :one
+SELECT * FROM authors
+WHERE id = $1 LIMIT 1;
+
+-- name: ListAuthors :many
+SELECT * FROM authors
+ORDER BY name;
+
+-- name: CreateAuthor :one
+INSERT INTO authors (
+          name, bio
+) VALUES (
+  $1, $2
+)
+RETURNING *;
+
+-- name: DeleteAuthor :exec
+DELETE FROM authors
+WHERE id = $1;
diff --git a/internal/endtoend/testdata/process_plugin_format_json/schema.sql b/internal/endtoend/testdata/process_plugin_format_json/schema.sql
new file mode 100644
index 000000000..b4fad7849
--- /dev/null
+++ b/internal/endtoend/testdata/process_plugin_format_json/schema.sql
@@ -0,0 +1,5 @@
+CREATE TABLE authors (
+          id   BIGSERIAL PRIMARY KEY,
+          name text      NOT NULL,
+          bio  text
+);
diff --git a/internal/endtoend/testdata/process_plugin_format_json/sqlc.json b/internal/endtoend/testdata/process_plugin_format_json/sqlc.json
new file mode 100644
index 000000000..28055fa57
--- /dev/null
+++ b/internal/endtoend/testdata/process_plugin_format_json/sqlc.json
@@ -0,0 +1,25 @@
+{
+  "version": "2",
+  "sql": [
+    {
+      "schema": "schema.sql",
+      "queries": "query.sql",
+      "engine": "postgresql",
+      "codegen": [
+        {
+          "out": "gen",
+          "plugin": "jsonb"
+        }
+      ]
+    }
+  ],
+  "plugins": [
+    {
+      "name": "jsonb",
+      "process": {
+        "cmd": "test-json-process-plugin",
+        "format": "json"
+      }
+    }
+  ]
+}
diff --git a/internal/sqltest/sqlite.go b/internal/sqltest/sqlite.go
index 72a154d0e..0e5161967 100644
--- a/internal/sqltest/sqlite.go
+++ b/internal/sqltest/sqlite.go
@@ -16,6 +16,9 @@ func SQLite(t *testing.T, migrations []string) (*sql.DB, func()) {
 	if err != nil {
 		t.Fatal(err)
 	}
+	if err := source.Close(); err != nil {
+		t.Fatal(err)
+	}
 	return CreateSQLiteDatabase(t, source.Name(), migrations)
 }
 
diff --git a/scripts/test-json-process-plugin/main.go b/scripts/test-json-process-plugin/main.go
new file mode 100644
index 000000000..6bcc7a25d
--- /dev/null
+++ b/scripts/test-json-process-plugin/main.go
@@ -0,0 +1,39 @@
+package main
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"os"
+)
+
+type Out struct {
+	Files []File `json:"files"`
+}
+
+type File struct {
+	Name     string `json:"name"`
+	Contents []byte `json:"contents"`
+}
+
+func main() {
+	in := make(map[string]interface{})
+	decoder := json.NewDecoder(os.Stdin)
+	err := decoder.Decode(&in)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "error generating JSON: %s", err)
+		os.Exit(2)
+	}
+
+	buf := bytes.NewBuffer(nil)
+	queries := in["queries"].([]interface{})
+	for _, q := range queries {
+		text := q.(map[string]interface{})["text"].(string)
+		buf.WriteString(text)
+		buf.WriteString("\n")
+	}
+
+	e := json.NewEncoder(os.Stdout)
+	e.SetIndent("", "  ")
+	e.Encode(&Out{Files: []File{{Name: "hello.txt", Contents: buf.Bytes()}}})
+}