From 2ea9d7a84e6d5847faa0a08ccf02b7f54f541287 Mon Sep 17 00:00:00 2001 From: Jeremy <87028711+jgoldberger26@users.noreply.github.com> Date: Sun, 22 Feb 2026 21:24:41 -0500 Subject: [PATCH 1/8] Change to postgres 18 --- .github/workflows/ci.yml | 2 +- README.md | 2 +- docker-compose.yml | 2 +- internal/testutils/container.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ff3d935..cc63677 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ jobs: services: # Optional: We use testcontainers, but having a service is good for fallback or specific DB tests postgres: - image: postgres:16-alpine + image: postgres:18-alpine env: POSTGRES_USER: test POSTGRES_PASSWORD: test diff --git a/README.md b/README.md index 3c16334..70fb4aa 100644 --- a/README.md +++ b/README.md @@ -180,7 +180,7 @@ To run the full stack (API + Postgres + Cloudflare Tunnel), update your `.env` f ```yaml services: db: - image: postgres:16-alpine + image: postgres:18-alpine env_file: - .env environment: diff --git a/docker-compose.yml b/docker-compose.yml index 9f5f0a7..bfaf25d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,6 @@ services: db: - image: postgres:16-alpine + image: postgres:18-alpine env_file: - .env environment: diff --git a/internal/testutils/container.go b/internal/testutils/container.go index 7cfdd0b..e2cf174 100644 --- a/internal/testutils/container.go +++ b/internal/testutils/container.go @@ -24,7 +24,7 @@ func SetupTestDB(t *testing.T) *pgxpool.Pool { schemaPath := filepath.Join(projectRoot, "schema.sql") pgContainer, err := postgres.Run(ctx, - "postgres:16-alpine", + "postgres:18-alpine", postgres.WithInitScripts(schemaPath), postgres.WithDatabase("test_db"), postgres.WithUsername("test"), From 64b200d5aa46ec369c72a95ea6e53e9cff94d685 Mon Sep 17 00:00:00 2001 From: Jeremy <87028711+jgoldberger26@users.noreply.github.com> Date: Sun, 22 Feb 2026 21:26:30 -0500 Subject: [PATCH 2/8] Add link shortener and tracking --- docs/schema/schema.json | 3344 ++++++++++++++++++++-------- go.mod | 6 + go.sum | 43 + internal/database/mocks/Querier.go | 216 ++ internal/database/models.go | 15 + internal/database/querier.go | 9 + internal/database/queries.sql | 34 + internal/database/queries.sql.go | 161 ++ internal/dto/dto.go | 28 + internal/handler/links.go | 311 +++ internal/handler/links_test.go | 180 ++ internal/router/router.go | 12 + schema.sql | 23 +- tests/benchmarks/suite_test.go | 2 +- 14 files changed, 3463 insertions(+), 921 deletions(-) create mode 100644 internal/handler/links.go create mode 100644 internal/handler/links_test.go diff --git a/docs/schema/schema.json b/docs/schema/schema.json index 67eaee4..f26baa0 100644 --- a/docs/schema/schema.json +++ b/docs/schema/schema.json @@ -1135,85 +1135,33 @@ } ], "comment": "" - } - ], - "enums": [ - { - "name": "user_role", - "vals": [ - "student", - "alumni", - "faculty", - "external" - ], - "comment": "" - } - ], - "composite_types": [] - }, - { - "comment": "", - "name": "pg_temp", - "tables": [], - "enums": [], - "composite_types": [] - }, - { - "comment": "", - "name": "pg_catalog", - "tables": [ + }, { "rel": { - "catalog": "pg_catalog", - "schema": "pg_catalog", - "name": "pg_aggregate" + "catalog": "", + "schema": "", + "name": "links" }, "columns": [ { - "name": "tableoid", + "name": "lid", "not_null": true, "is_array": false, "comment": "", - "length": 4, + "length": -1, "is_named_param": false, "is_func_call": false, "scope": "", "table": { - "catalog": "pg_catalog", - "schema": "pg_catalog", - "name": "pg_aggregate" - }, - "table_alias": "", - "type": { "catalog": "", "schema": "", - "name": "oid" - }, - "is_sqlc_slice": false, - "embed_table": null, - "original_name": "", - "unsigned": false, - "array_dims": 0 - }, - { - "name": "cmax", - "not_null": true, - "is_array": false, - "comment": "", - "length": 4, - "is_named_param": false, - "is_func_call": false, - "scope": "", - "table": { - "catalog": "pg_catalog", - "schema": "pg_catalog", - "name": "pg_aggregate" + "name": "links" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "cid" + "name": "uuid" }, "is_sqlc_slice": false, "embed_table": null, @@ -1222,24 +1170,24 @@ "array_dims": 0 }, { - "name": "xmax", + "name": "endpoint_url", "not_null": true, "is_array": false, "comment": "", - "length": 4, + "length": -1, "is_named_param": false, "is_func_call": false, "scope": "", "table": { - "catalog": "pg_catalog", - "schema": "pg_catalog", - "name": "pg_aggregate" + "catalog": "", + "schema": "", + "name": "links" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "xid" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, @@ -1248,24 +1196,24 @@ "array_dims": 0 }, { - "name": "cmin", + "name": "dest_url", "not_null": true, "is_array": false, "comment": "", - "length": 4, + "length": -1, "is_named_param": false, "is_func_call": false, "scope": "", "table": { - "catalog": "pg_catalog", - "schema": "pg_catalog", - "name": "pg_aggregate" + "catalog": "", + "schema": "", + "name": "links" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "cid" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, @@ -1274,24 +1222,24 @@ "array_dims": 0 }, { - "name": "xmin", + "name": "oid", "not_null": true, "is_array": false, "comment": "", - "length": 4, + "length": -1, "is_named_param": false, "is_func_call": false, "scope": "", "table": { - "catalog": "pg_catalog", - "schema": "pg_catalog", - "name": "pg_aggregate" + "catalog": "", + "schema": "", + "name": "links" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "xid" + "name": "uuid" }, "is_sqlc_slice": false, "embed_table": null, @@ -1300,50 +1248,60 @@ "array_dims": 0 }, { - "name": "ctid", - "not_null": true, + "name": "created_at", + "not_null": false, "is_array": false, "comment": "", - "length": 6, + "length": -1, "is_named_param": false, "is_func_call": false, "scope": "", "table": { - "catalog": "pg_catalog", - "schema": "pg_catalog", - "name": "pg_aggregate" + "catalog": "", + "schema": "", + "name": "links" }, "table_alias": "", "type": { "catalog": "", - "schema": "", - "name": "tid" + "schema": "pg_catalog", + "name": "timestamp" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, "array_dims": 0 - }, + } + ], + "comment": "" + }, + { + "rel": { + "catalog": "", + "schema": "", + "name": "link_visits" + }, + "columns": [ { - "name": "aggfnoid", + "name": "lvid", "not_null": true, "is_array": false, "comment": "", - "length": 4, + "length": -1, "is_named_param": false, "is_func_call": false, "scope": "", "table": { - "catalog": "pg_catalog", - "schema": "pg_catalog", - "name": "pg_aggregate" + "catalog": "", + "schema": "", + "name": "link_visits" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "regproc" + "name": "uuid" }, "is_sqlc_slice": false, "embed_table": null, @@ -1352,24 +1310,24 @@ "array_dims": 0 }, { - "name": "aggkind", + "name": "lid", "not_null": true, "is_array": false, "comment": "", - "length": 1, + "length": -1, "is_named_param": false, "is_func_call": false, "scope": "", "table": { - "catalog": "pg_catalog", - "schema": "pg_catalog", - "name": "pg_aggregate" + "catalog": "", + "schema": "", + "name": "link_visits" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "char" + "name": "uuid" }, "is_sqlc_slice": false, "embed_table": null, @@ -1378,24 +1336,24 @@ "array_dims": 0 }, { - "name": "aggnumdirectargs", - "not_null": true, + "name": "uid", + "not_null": false, "is_array": false, "comment": "", - "length": 2, + "length": -1, "is_named_param": false, "is_func_call": false, "scope": "", "table": { - "catalog": "pg_catalog", - "schema": "pg_catalog", - "name": "pg_aggregate" + "catalog": "", + "schema": "", + "name": "link_visits" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "int2" + "name": "uuid" }, "is_sqlc_slice": false, "embed_table": null, @@ -1404,33 +1362,69 @@ "array_dims": 0 }, { - "name": "aggtransfn", - "not_null": true, + "name": "created_at", + "not_null": false, "is_array": false, "comment": "", - "length": 4, + "length": -1, "is_named_param": false, "is_func_call": false, "scope": "", "table": { - "catalog": "pg_catalog", - "schema": "pg_catalog", - "name": "pg_aggregate" + "catalog": "", + "schema": "", + "name": "link_visits" }, "table_alias": "", "type": { "catalog": "", - "schema": "", - "name": "regproc" + "schema": "pg_catalog", + "name": "timestamp" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, "array_dims": 0 - }, + } + ], + "comment": "" + } + ], + "enums": [ + { + "name": "user_role", + "vals": [ + "student", + "alumni", + "faculty", + "external" + ], + "comment": "" + } + ], + "composite_types": [] + }, + { + "comment": "", + "name": "pg_temp", + "tables": [], + "enums": [], + "composite_types": [] + }, + { + "comment": "", + "name": "pg_catalog", + "tables": [ + { + "rel": { + "catalog": "pg_catalog", + "schema": "pg_catalog", + "name": "pg_aggregate" + }, + "columns": [ { - "name": "aggfinalfn", + "name": "tableoid", "not_null": true, "is_array": false, "comment": "", @@ -1447,7 +1441,7 @@ "type": { "catalog": "", "schema": "", - "name": "regproc" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, @@ -1456,7 +1450,7 @@ "array_dims": 0 }, { - "name": "aggcombinefn", + "name": "cmax", "not_null": true, "is_array": false, "comment": "", @@ -1473,7 +1467,7 @@ "type": { "catalog": "", "schema": "", - "name": "regproc" + "name": "cid" }, "is_sqlc_slice": false, "embed_table": null, @@ -1482,7 +1476,7 @@ "array_dims": 0 }, { - "name": "aggserialfn", + "name": "xmax", "not_null": true, "is_array": false, "comment": "", @@ -1499,7 +1493,7 @@ "type": { "catalog": "", "schema": "", - "name": "regproc" + "name": "xid" }, "is_sqlc_slice": false, "embed_table": null, @@ -1508,7 +1502,7 @@ "array_dims": 0 }, { - "name": "aggdeserialfn", + "name": "cmin", "not_null": true, "is_array": false, "comment": "", @@ -1525,7 +1519,7 @@ "type": { "catalog": "", "schema": "", - "name": "regproc" + "name": "cid" }, "is_sqlc_slice": false, "embed_table": null, @@ -1534,7 +1528,7 @@ "array_dims": 0 }, { - "name": "aggmtransfn", + "name": "xmin", "not_null": true, "is_array": false, "comment": "", @@ -1551,7 +1545,7 @@ "type": { "catalog": "", "schema": "", - "name": "regproc" + "name": "xid" }, "is_sqlc_slice": false, "embed_table": null, @@ -1560,11 +1554,11 @@ "array_dims": 0 }, { - "name": "aggminvtransfn", + "name": "ctid", "not_null": true, "is_array": false, "comment": "", - "length": 4, + "length": 6, "is_named_param": false, "is_func_call": false, "scope": "", @@ -1577,7 +1571,7 @@ "type": { "catalog": "", "schema": "", - "name": "regproc" + "name": "tid" }, "is_sqlc_slice": false, "embed_table": null, @@ -1586,7 +1580,7 @@ "array_dims": 0 }, { - "name": "aggmfinalfn", + "name": "aggfnoid", "not_null": true, "is_array": false, "comment": "", @@ -1612,7 +1606,7 @@ "array_dims": 0 }, { - "name": "aggfinalextra", + "name": "aggkind", "not_null": true, "is_array": false, "comment": "", @@ -1629,7 +1623,7 @@ "type": { "catalog": "", "schema": "", - "name": "bool" + "name": "char" }, "is_sqlc_slice": false, "embed_table": null, @@ -1638,11 +1632,11 @@ "array_dims": 0 }, { - "name": "aggmfinalextra", + "name": "aggnumdirectargs", "not_null": true, "is_array": false, "comment": "", - "length": 1, + "length": 2, "is_named_param": false, "is_func_call": false, "scope": "", @@ -1655,7 +1649,7 @@ "type": { "catalog": "", "schema": "", - "name": "bool" + "name": "int2" }, "is_sqlc_slice": false, "embed_table": null, @@ -1664,11 +1658,11 @@ "array_dims": 0 }, { - "name": "aggfinalmodify", + "name": "aggtransfn", "not_null": true, "is_array": false, "comment": "", - "length": 1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -1681,7 +1675,7 @@ "type": { "catalog": "", "schema": "", - "name": "char" + "name": "regproc" }, "is_sqlc_slice": false, "embed_table": null, @@ -1690,11 +1684,11 @@ "array_dims": 0 }, { - "name": "aggmfinalmodify", + "name": "aggfinalfn", "not_null": true, "is_array": false, "comment": "", - "length": 1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -1707,7 +1701,7 @@ "type": { "catalog": "", "schema": "", - "name": "char" + "name": "regproc" }, "is_sqlc_slice": false, "embed_table": null, @@ -1716,7 +1710,7 @@ "array_dims": 0 }, { - "name": "aggsortop", + "name": "aggcombinefn", "not_null": true, "is_array": false, "comment": "", @@ -1733,7 +1727,7 @@ "type": { "catalog": "", "schema": "", - "name": "oid" + "name": "regproc" }, "is_sqlc_slice": false, "embed_table": null, @@ -1742,7 +1736,7 @@ "array_dims": 0 }, { - "name": "aggtranstype", + "name": "aggserialfn", "not_null": true, "is_array": false, "comment": "", @@ -1759,7 +1753,7 @@ "type": { "catalog": "", "schema": "", - "name": "oid" + "name": "regproc" }, "is_sqlc_slice": false, "embed_table": null, @@ -1768,7 +1762,7 @@ "array_dims": 0 }, { - "name": "aggtransspace", + "name": "aggdeserialfn", "not_null": true, "is_array": false, "comment": "", @@ -1785,7 +1779,7 @@ "type": { "catalog": "", "schema": "", - "name": "int4" + "name": "regproc" }, "is_sqlc_slice": false, "embed_table": null, @@ -1794,7 +1788,7 @@ "array_dims": 0 }, { - "name": "aggmtranstype", + "name": "aggmtransfn", "not_null": true, "is_array": false, "comment": "", @@ -1811,7 +1805,7 @@ "type": { "catalog": "", "schema": "", - "name": "oid" + "name": "regproc" }, "is_sqlc_slice": false, "embed_table": null, @@ -1820,7 +1814,7 @@ "array_dims": 0 }, { - "name": "aggmtransspace", + "name": "aggminvtransfn", "not_null": true, "is_array": false, "comment": "", @@ -1837,7 +1831,7 @@ "type": { "catalog": "", "schema": "", - "name": "int4" + "name": "regproc" }, "is_sqlc_slice": false, "embed_table": null, @@ -1846,11 +1840,11 @@ "array_dims": 0 }, { - "name": "agginitval", - "not_null": false, + "name": "aggmfinalfn", + "not_null": true, "is_array": false, "comment": "", - "length": -1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -1863,7 +1857,7 @@ "type": { "catalog": "", "schema": "", - "name": "text" + "name": "regproc" }, "is_sqlc_slice": false, "embed_table": null, @@ -1872,11 +1866,11 @@ "array_dims": 0 }, { - "name": "aggminitval", - "not_null": false, + "name": "aggfinalextra", + "not_null": true, "is_array": false, "comment": "", - "length": -1, + "length": 1, "is_named_param": false, "is_func_call": false, "scope": "", @@ -1889,43 +1883,33 @@ "type": { "catalog": "", "schema": "", - "name": "text" + "name": "bool" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, "array_dims": 0 - } - ], - "comment": "" - }, - { - "rel": { - "catalog": "pg_catalog", - "schema": "pg_catalog", - "name": "pg_am" - }, - "columns": [ + }, { - "name": "tableoid", + "name": "aggmfinalextra", "not_null": true, "is_array": false, "comment": "", - "length": 4, + "length": 1, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_am" + "name": "pg_aggregate" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "oid" + "name": "bool" }, "is_sqlc_slice": false, "embed_table": null, @@ -1934,24 +1918,24 @@ "array_dims": 0 }, { - "name": "cmax", + "name": "aggfinalmodify", "not_null": true, "is_array": false, "comment": "", - "length": 4, + "length": 1, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_am" + "name": "pg_aggregate" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "cid" + "name": "char" }, "is_sqlc_slice": false, "embed_table": null, @@ -1960,24 +1944,24 @@ "array_dims": 0 }, { - "name": "xmax", + "name": "aggmfinalmodify", "not_null": true, "is_array": false, "comment": "", - "length": 4, + "length": 1, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_am" + "name": "pg_aggregate" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "xid" + "name": "char" }, "is_sqlc_slice": false, "embed_table": null, @@ -1986,7 +1970,7 @@ "array_dims": 0 }, { - "name": "cmin", + "name": "aggsortop", "not_null": true, "is_array": false, "comment": "", @@ -1997,13 +1981,13 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_am" + "name": "pg_aggregate" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "cid" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, @@ -2012,7 +1996,7 @@ "array_dims": 0 }, { - "name": "xmin", + "name": "aggtranstype", "not_null": true, "is_array": false, "comment": "", @@ -2023,13 +2007,13 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_am" + "name": "pg_aggregate" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "xid" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, @@ -2038,24 +2022,24 @@ "array_dims": 0 }, { - "name": "ctid", + "name": "aggtransspace", "not_null": true, "is_array": false, "comment": "", - "length": 6, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_am" + "name": "pg_aggregate" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "tid" + "name": "int4" }, "is_sqlc_slice": false, "embed_table": null, @@ -2064,7 +2048,7 @@ "array_dims": 0 }, { - "name": "oid", + "name": "aggmtranstype", "not_null": true, "is_array": false, "comment": "", @@ -2075,7 +2059,7 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_am" + "name": "pg_aggregate" }, "table_alias": "", "type": { @@ -2090,24 +2074,24 @@ "array_dims": 0 }, { - "name": "amname", + "name": "aggmtransspace", "not_null": true, "is_array": false, "comment": "", - "length": 64, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_am" + "name": "pg_aggregate" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "name" + "name": "int4" }, "is_sqlc_slice": false, "embed_table": null, @@ -2116,24 +2100,24 @@ "array_dims": 0 }, { - "name": "amhandler", - "not_null": true, + "name": "agginitval", + "not_null": false, "is_array": false, "comment": "", - "length": 4, + "length": -1, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_am" + "name": "pg_aggregate" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "regproc" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, @@ -2142,24 +2126,24 @@ "array_dims": 0 }, { - "name": "amtype", - "not_null": true, + "name": "aggminitval", + "not_null": false, "is_array": false, "comment": "", - "length": 1, + "length": -1, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_am" + "name": "pg_aggregate" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "char" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, @@ -2174,7 +2158,7 @@ "rel": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_amop" + "name": "pg_am" }, "columns": [ { @@ -2189,7 +2173,7 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_amop" + "name": "pg_am" }, "table_alias": "", "type": { @@ -2215,7 +2199,7 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_amop" + "name": "pg_am" }, "table_alias": "", "type": { @@ -2241,7 +2225,7 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_amop" + "name": "pg_am" }, "table_alias": "", "type": { @@ -2267,7 +2251,7 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_amop" + "name": "pg_am" }, "table_alias": "", "type": { @@ -2293,7 +2277,7 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_amop" + "name": "pg_am" }, "table_alias": "", "type": { @@ -2319,7 +2303,7 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_amop" + "name": "pg_am" }, "table_alias": "", "type": { @@ -2345,7 +2329,7 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_amop" + "name": "pg_am" }, "table_alias": "", "type": { @@ -2360,7 +2344,33 @@ "array_dims": 0 }, { - "name": "amopfamily", + "name": "amname", + "not_null": true, + "is_array": false, + "comment": "", + "length": 64, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "pg_catalog", + "schema": "pg_catalog", + "name": "pg_am" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "name" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "", + "unsigned": false, + "array_dims": 0 + }, + { + "name": "amhandler", "not_null": true, "is_array": false, "comment": "", @@ -2371,13 +2381,13 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_amop" + "name": "pg_am" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "oid" + "name": "regproc" }, "is_sqlc_slice": false, "embed_table": null, @@ -2386,7 +2396,43 @@ "array_dims": 0 }, { - "name": "amoplefttype", + "name": "amtype", + "not_null": true, + "is_array": false, + "comment": "", + "length": 1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "pg_catalog", + "schema": "pg_catalog", + "name": "pg_am" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "char" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "", + "unsigned": false, + "array_dims": 0 + } + ], + "comment": "" + }, + { + "rel": { + "catalog": "pg_catalog", + "schema": "pg_catalog", + "name": "pg_amop" + }, + "columns": [ + { + "name": "tableoid", "not_null": true, "is_array": false, "comment": "", @@ -2412,7 +2458,7 @@ "array_dims": 0 }, { - "name": "amoprighttype", + "name": "cmax", "not_null": true, "is_array": false, "comment": "", @@ -2429,7 +2475,7 @@ "type": { "catalog": "", "schema": "", - "name": "oid" + "name": "cid" }, "is_sqlc_slice": false, "embed_table": null, @@ -2438,11 +2484,11 @@ "array_dims": 0 }, { - "name": "amopstrategy", + "name": "xmax", "not_null": true, "is_array": false, "comment": "", - "length": 2, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -2455,7 +2501,7 @@ "type": { "catalog": "", "schema": "", - "name": "int2" + "name": "xid" }, "is_sqlc_slice": false, "embed_table": null, @@ -2464,11 +2510,11 @@ "array_dims": 0 }, { - "name": "amoppurpose", + "name": "cmin", "not_null": true, "is_array": false, "comment": "", - "length": 1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -2481,7 +2527,7 @@ "type": { "catalog": "", "schema": "", - "name": "char" + "name": "cid" }, "is_sqlc_slice": false, "embed_table": null, @@ -2490,7 +2536,7 @@ "array_dims": 0 }, { - "name": "amopopr", + "name": "xmin", "not_null": true, "is_array": false, "comment": "", @@ -2507,7 +2553,7 @@ "type": { "catalog": "", "schema": "", - "name": "oid" + "name": "xid" }, "is_sqlc_slice": false, "embed_table": null, @@ -2516,7 +2562,33 @@ "array_dims": 0 }, { - "name": "amopmethod", + "name": "ctid", + "not_null": true, + "is_array": false, + "comment": "", + "length": 6, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "pg_catalog", + "schema": "pg_catalog", + "name": "pg_amop" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "tid" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "", + "unsigned": false, + "array_dims": 0 + }, + { + "name": "oid", "not_null": true, "is_array": false, "comment": "", @@ -2542,7 +2614,7 @@ "array_dims": 0 }, { - "name": "amopsortfamily", + "name": "amopfamily", "not_null": true, "is_array": false, "comment": "", @@ -2566,19 +2638,9 @@ "original_name": "", "unsigned": false, "array_dims": 0 - } - ], - "comment": "" - }, - { - "rel": { - "catalog": "pg_catalog", - "schema": "pg_catalog", - "name": "pg_amproc" - }, - "columns": [ + }, { - "name": "tableoid", + "name": "amoplefttype", "not_null": true, "is_array": false, "comment": "", @@ -2589,7 +2651,7 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_amproc" + "name": "pg_amop" }, "table_alias": "", "type": { @@ -2604,7 +2666,7 @@ "array_dims": 0 }, { - "name": "cmax", + "name": "amoprighttype", "not_null": true, "is_array": false, "comment": "", @@ -2615,13 +2677,13 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_amproc" + "name": "pg_amop" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "cid" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, @@ -2630,24 +2692,24 @@ "array_dims": 0 }, { - "name": "xmax", + "name": "amopstrategy", "not_null": true, "is_array": false, "comment": "", - "length": 4, + "length": 2, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_amproc" + "name": "pg_amop" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "xid" + "name": "int2" }, "is_sqlc_slice": false, "embed_table": null, @@ -2656,24 +2718,24 @@ "array_dims": 0 }, { - "name": "cmin", + "name": "amoppurpose", "not_null": true, "is_array": false, "comment": "", - "length": 4, + "length": 1, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_amproc" + "name": "pg_amop" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "cid" + "name": "char" }, "is_sqlc_slice": false, "embed_table": null, @@ -2682,7 +2744,7 @@ "array_dims": 0 }, { - "name": "xmin", + "name": "amopopr", "not_null": true, "is_array": false, "comment": "", @@ -2693,13 +2755,13 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_amproc" + "name": "pg_amop" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "xid" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, @@ -2708,24 +2770,24 @@ "array_dims": 0 }, { - "name": "ctid", + "name": "amopmethod", "not_null": true, "is_array": false, "comment": "", - "length": 6, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_amproc" + "name": "pg_amop" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "tid" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, @@ -2734,7 +2796,7 @@ "array_dims": 0 }, { - "name": "oid", + "name": "amopsortfamily", "not_null": true, "is_array": false, "comment": "", @@ -2745,7 +2807,7 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_amproc" + "name": "pg_amop" }, "table_alias": "", "type": { @@ -2758,9 +2820,19 @@ "original_name": "", "unsigned": false, "array_dims": 0 - }, + } + ], + "comment": "" + }, + { + "rel": { + "catalog": "pg_catalog", + "schema": "pg_catalog", + "name": "pg_amproc" + }, + "columns": [ { - "name": "amprocfamily", + "name": "tableoid", "not_null": true, "is_array": false, "comment": "", @@ -2786,7 +2858,7 @@ "array_dims": 0 }, { - "name": "amproclefttype", + "name": "cmax", "not_null": true, "is_array": false, "comment": "", @@ -2803,7 +2875,7 @@ "type": { "catalog": "", "schema": "", - "name": "oid" + "name": "cid" }, "is_sqlc_slice": false, "embed_table": null, @@ -2812,7 +2884,7 @@ "array_dims": 0 }, { - "name": "amprocrighttype", + "name": "xmax", "not_null": true, "is_array": false, "comment": "", @@ -2829,7 +2901,7 @@ "type": { "catalog": "", "schema": "", - "name": "oid" + "name": "xid" }, "is_sqlc_slice": false, "embed_table": null, @@ -2838,11 +2910,11 @@ "array_dims": 0 }, { - "name": "amprocnum", + "name": "cmin", "not_null": true, "is_array": false, "comment": "", - "length": 2, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -2855,7 +2927,7 @@ "type": { "catalog": "", "schema": "", - "name": "int2" + "name": "cid" }, "is_sqlc_slice": false, "embed_table": null, @@ -2864,7 +2936,7 @@ "array_dims": 0 }, { - "name": "amproc", + "name": "xmin", "not_null": true, "is_array": false, "comment": "", @@ -2881,43 +2953,33 @@ "type": { "catalog": "", "schema": "", - "name": "regproc" + "name": "xid" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, "array_dims": 0 - } - ], - "comment": "" - }, - { - "rel": { - "catalog": "pg_catalog", - "schema": "pg_catalog", - "name": "pg_attrdef" - }, - "columns": [ + }, { - "name": "tableoid", + "name": "ctid", "not_null": true, "is_array": false, "comment": "", - "length": 4, + "length": 6, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_attrdef" + "name": "pg_amproc" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "oid" + "name": "tid" }, "is_sqlc_slice": false, "embed_table": null, @@ -2926,7 +2988,7 @@ "array_dims": 0 }, { - "name": "cmax", + "name": "oid", "not_null": true, "is_array": false, "comment": "", @@ -2937,13 +2999,13 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_attrdef" + "name": "pg_amproc" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "cid" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, @@ -2952,7 +3014,7 @@ "array_dims": 0 }, { - "name": "xmax", + "name": "amprocfamily", "not_null": true, "is_array": false, "comment": "", @@ -2963,13 +3025,13 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_attrdef" + "name": "pg_amproc" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "xid" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, @@ -2978,7 +3040,7 @@ "array_dims": 0 }, { - "name": "cmin", + "name": "amproclefttype", "not_null": true, "is_array": false, "comment": "", @@ -2989,13 +3051,13 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_attrdef" + "name": "pg_amproc" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "cid" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, @@ -3004,7 +3066,7 @@ "array_dims": 0 }, { - "name": "xmin", + "name": "amprocrighttype", "not_null": true, "is_array": false, "comment": "", @@ -3015,13 +3077,13 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_attrdef" + "name": "pg_amproc" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "xid" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, @@ -3030,24 +3092,24 @@ "array_dims": 0 }, { - "name": "ctid", + "name": "amprocnum", "not_null": true, "is_array": false, "comment": "", - "length": 6, + "length": 2, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_attrdef" + "name": "pg_amproc" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "tid" + "name": "int2" }, "is_sqlc_slice": false, "embed_table": null, @@ -3056,7 +3118,7 @@ "array_dims": 0 }, { - "name": "oid", + "name": "amproc", "not_null": true, "is_array": false, "comment": "", @@ -3067,22 +3129,32 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_attrdef" + "name": "pg_amproc" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "oid" + "name": "regproc" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, "array_dims": 0 - }, + } + ], + "comment": "" + }, + { + "rel": { + "catalog": "pg_catalog", + "schema": "pg_catalog", + "name": "pg_attrdef" + }, + "columns": [ { - "name": "adrelid", + "name": "tableoid", "not_null": true, "is_array": false, "comment": "", @@ -3107,94 +3179,6 @@ "unsigned": false, "array_dims": 0 }, - { - "name": "adnum", - "not_null": true, - "is_array": false, - "comment": "", - "length": 2, - "is_named_param": false, - "is_func_call": false, - "scope": "", - "table": { - "catalog": "pg_catalog", - "schema": "pg_catalog", - "name": "pg_attrdef" - }, - "table_alias": "", - "type": { - "catalog": "", - "schema": "", - "name": "int2" - }, - "is_sqlc_slice": false, - "embed_table": null, - "original_name": "", - "unsigned": false, - "array_dims": 0 - }, - { - "name": "adbin", - "not_null": true, - "is_array": false, - "comment": "", - "length": -1, - "is_named_param": false, - "is_func_call": false, - "scope": "", - "table": { - "catalog": "pg_catalog", - "schema": "pg_catalog", - "name": "pg_attrdef" - }, - "table_alias": "", - "type": { - "catalog": "", - "schema": "", - "name": "pg_node_tree" - }, - "is_sqlc_slice": false, - "embed_table": null, - "original_name": "", - "unsigned": false, - "array_dims": 0 - } - ], - "comment": "" - }, - { - "rel": { - "catalog": "pg_catalog", - "schema": "pg_catalog", - "name": "pg_attribute" - }, - "columns": [ - { - "name": "tableoid", - "not_null": true, - "is_array": false, - "comment": "", - "length": 4, - "is_named_param": false, - "is_func_call": false, - "scope": "", - "table": { - "catalog": "pg_catalog", - "schema": "pg_catalog", - "name": "pg_attribute" - }, - "table_alias": "", - "type": { - "catalog": "", - "schema": "", - "name": "oid" - }, - "is_sqlc_slice": false, - "embed_table": null, - "original_name": "", - "unsigned": false, - "array_dims": 0 - }, { "name": "cmax", "not_null": true, @@ -3207,7 +3191,7 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_attribute" + "name": "pg_attrdef" }, "table_alias": "", "type": { @@ -3233,7 +3217,7 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_attribute" + "name": "pg_attrdef" }, "table_alias": "", "type": { @@ -3259,7 +3243,7 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_attribute" + "name": "pg_attrdef" }, "table_alias": "", "type": { @@ -3285,7 +3269,7 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_attribute" + "name": "pg_attrdef" }, "table_alias": "", "type": { @@ -3311,7 +3295,7 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_attribute" + "name": "pg_attrdef" }, "table_alias": "", "type": { @@ -3326,7 +3310,7 @@ "array_dims": 0 }, { - "name": "attrelid", + "name": "oid", "not_null": true, "is_array": false, "comment": "", @@ -3337,7 +3321,7 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_attribute" + "name": "pg_attrdef" }, "table_alias": "", "type": { @@ -3352,33 +3336,7 @@ "array_dims": 0 }, { - "name": "attname", - "not_null": true, - "is_array": false, - "comment": "", - "length": 64, - "is_named_param": false, - "is_func_call": false, - "scope": "", - "table": { - "catalog": "pg_catalog", - "schema": "pg_catalog", - "name": "pg_attribute" - }, - "table_alias": "", - "type": { - "catalog": "", - "schema": "", - "name": "name" - }, - "is_sqlc_slice": false, - "embed_table": null, - "original_name": "", - "unsigned": false, - "array_dims": 0 - }, - { - "name": "atttypid", + "name": "adrelid", "not_null": true, "is_array": false, "comment": "", @@ -3389,7 +3347,7 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_attribute" + "name": "pg_attrdef" }, "table_alias": "", "type": { @@ -3404,33 +3362,7 @@ "array_dims": 0 }, { - "name": "attstattarget", - "not_null": true, - "is_array": false, - "comment": "", - "length": 4, - "is_named_param": false, - "is_func_call": false, - "scope": "", - "table": { - "catalog": "pg_catalog", - "schema": "pg_catalog", - "name": "pg_attribute" - }, - "table_alias": "", - "type": { - "catalog": "", - "schema": "", - "name": "int4" - }, - "is_sqlc_slice": false, - "embed_table": null, - "original_name": "", - "unsigned": false, - "array_dims": 0 - }, - { - "name": "attlen", + "name": "adnum", "not_null": true, "is_array": false, "comment": "", @@ -3441,7 +3373,7 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_attribute" + "name": "pg_attrdef" }, "table_alias": "", "type": { @@ -3456,33 +3388,43 @@ "array_dims": 0 }, { - "name": "attnum", + "name": "adbin", "not_null": true, "is_array": false, "comment": "", - "length": 2, + "length": -1, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_attribute" + "name": "pg_attrdef" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "int2" + "name": "pg_node_tree" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, "array_dims": 0 - }, + } + ], + "comment": "" + }, + { + "rel": { + "catalog": "pg_catalog", + "schema": "pg_catalog", + "name": "pg_attribute" + }, + "columns": [ { - "name": "attndims", + "name": "tableoid", "not_null": true, "is_array": false, "comment": "", @@ -3499,7 +3441,7 @@ "type": { "catalog": "", "schema": "", - "name": "int4" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, @@ -3508,7 +3450,7 @@ "array_dims": 0 }, { - "name": "attcacheoff", + "name": "cmax", "not_null": true, "is_array": false, "comment": "", @@ -3525,7 +3467,7 @@ "type": { "catalog": "", "schema": "", - "name": "int4" + "name": "cid" }, "is_sqlc_slice": false, "embed_table": null, @@ -3534,7 +3476,7 @@ "array_dims": 0 }, { - "name": "atttypmod", + "name": "xmax", "not_null": true, "is_array": false, "comment": "", @@ -3551,7 +3493,7 @@ "type": { "catalog": "", "schema": "", - "name": "int4" + "name": "xid" }, "is_sqlc_slice": false, "embed_table": null, @@ -3560,11 +3502,11 @@ "array_dims": 0 }, { - "name": "attbyval", + "name": "cmin", "not_null": true, "is_array": false, "comment": "", - "length": 1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -3577,7 +3519,7 @@ "type": { "catalog": "", "schema": "", - "name": "bool" + "name": "cid" }, "is_sqlc_slice": false, "embed_table": null, @@ -3586,11 +3528,11 @@ "array_dims": 0 }, { - "name": "attalign", + "name": "xmin", "not_null": true, "is_array": false, "comment": "", - "length": 1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -3603,7 +3545,7 @@ "type": { "catalog": "", "schema": "", - "name": "char" + "name": "xid" }, "is_sqlc_slice": false, "embed_table": null, @@ -3612,11 +3554,11 @@ "array_dims": 0 }, { - "name": "attstorage", + "name": "ctid", "not_null": true, "is_array": false, "comment": "", - "length": 1, + "length": 6, "is_named_param": false, "is_func_call": false, "scope": "", @@ -3629,7 +3571,7 @@ "type": { "catalog": "", "schema": "", - "name": "char" + "name": "tid" }, "is_sqlc_slice": false, "embed_table": null, @@ -3638,11 +3580,11 @@ "array_dims": 0 }, { - "name": "attcompression", + "name": "attrelid", "not_null": true, "is_array": false, "comment": "", - "length": 1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -3655,7 +3597,7 @@ "type": { "catalog": "", "schema": "", - "name": "char" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, @@ -3664,11 +3606,11 @@ "array_dims": 0 }, { - "name": "attnotnull", + "name": "attname", "not_null": true, "is_array": false, "comment": "", - "length": 1, + "length": 64, "is_named_param": false, "is_func_call": false, "scope": "", @@ -3681,7 +3623,7 @@ "type": { "catalog": "", "schema": "", - "name": "bool" + "name": "name" }, "is_sqlc_slice": false, "embed_table": null, @@ -3690,11 +3632,11 @@ "array_dims": 0 }, { - "name": "atthasdef", + "name": "atttypid", "not_null": true, "is_array": false, "comment": "", - "length": 1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -3707,7 +3649,7 @@ "type": { "catalog": "", "schema": "", - "name": "bool" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, @@ -3716,11 +3658,11 @@ "array_dims": 0 }, { - "name": "atthasmissing", + "name": "attstattarget", "not_null": true, "is_array": false, "comment": "", - "length": 1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -3733,7 +3675,7 @@ "type": { "catalog": "", "schema": "", - "name": "bool" + "name": "int4" }, "is_sqlc_slice": false, "embed_table": null, @@ -3742,11 +3684,11 @@ "array_dims": 0 }, { - "name": "attidentity", + "name": "attlen", "not_null": true, "is_array": false, "comment": "", - "length": 1, + "length": 2, "is_named_param": false, "is_func_call": false, "scope": "", @@ -3759,7 +3701,7 @@ "type": { "catalog": "", "schema": "", - "name": "char" + "name": "int2" }, "is_sqlc_slice": false, "embed_table": null, @@ -3768,11 +3710,11 @@ "array_dims": 0 }, { - "name": "attgenerated", + "name": "attnum", "not_null": true, "is_array": false, "comment": "", - "length": 1, + "length": 2, "is_named_param": false, "is_func_call": false, "scope": "", @@ -3785,7 +3727,7 @@ "type": { "catalog": "", "schema": "", - "name": "char" + "name": "int2" }, "is_sqlc_slice": false, "embed_table": null, @@ -3794,11 +3736,11 @@ "array_dims": 0 }, { - "name": "attisdropped", + "name": "attndims", "not_null": true, "is_array": false, "comment": "", - "length": 1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -3811,7 +3753,7 @@ "type": { "catalog": "", "schema": "", - "name": "bool" + "name": "int4" }, "is_sqlc_slice": false, "embed_table": null, @@ -3820,11 +3762,11 @@ "array_dims": 0 }, { - "name": "attislocal", + "name": "attcacheoff", "not_null": true, "is_array": false, "comment": "", - "length": 1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -3837,7 +3779,7 @@ "type": { "catalog": "", "schema": "", - "name": "bool" + "name": "int4" }, "is_sqlc_slice": false, "embed_table": null, @@ -3846,7 +3788,7 @@ "array_dims": 0 }, { - "name": "attinhcount", + "name": "atttypmod", "not_null": true, "is_array": false, "comment": "", @@ -3872,11 +3814,11 @@ "array_dims": 0 }, { - "name": "attcollation", + "name": "attbyval", "not_null": true, "is_array": false, "comment": "", - "length": 4, + "length": 1, "is_named_param": false, "is_func_call": false, "scope": "", @@ -3889,7 +3831,7 @@ "type": { "catalog": "", "schema": "", - "name": "oid" + "name": "bool" }, "is_sqlc_slice": false, "embed_table": null, @@ -3898,11 +3840,11 @@ "array_dims": 0 }, { - "name": "attacl", - "not_null": false, - "is_array": true, + "name": "attalign", + "not_null": true, + "is_array": false, "comment": "", - "length": -1, + "length": 1, "is_named_param": false, "is_func_call": false, "scope": "", @@ -3915,7 +3857,7 @@ "type": { "catalog": "", "schema": "", - "name": "_aclitem" + "name": "char" }, "is_sqlc_slice": false, "embed_table": null, @@ -3924,11 +3866,11 @@ "array_dims": 0 }, { - "name": "attoptions", - "not_null": false, - "is_array": true, + "name": "attstorage", + "not_null": true, + "is_array": false, "comment": "", - "length": -1, + "length": 1, "is_named_param": false, "is_func_call": false, "scope": "", @@ -3941,7 +3883,7 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "char" }, "is_sqlc_slice": false, "embed_table": null, @@ -3950,11 +3892,11 @@ "array_dims": 0 }, { - "name": "attfdwoptions", - "not_null": false, - "is_array": true, + "name": "attcompression", + "not_null": true, + "is_array": false, "comment": "", - "length": -1, + "length": 1, "is_named_param": false, "is_func_call": false, "scope": "", @@ -3967,7 +3909,7 @@ "type": { "catalog": "", "schema": "", - "name": "_text" + "name": "char" }, "is_sqlc_slice": false, "embed_table": null, @@ -3976,11 +3918,11 @@ "array_dims": 0 }, { - "name": "attmissingval", - "not_null": false, + "name": "attnotnull", + "not_null": true, "is_array": false, "comment": "", - "length": -1, + "length": 1, "is_named_param": false, "is_func_call": false, "scope": "", @@ -3993,43 +3935,33 @@ "type": { "catalog": "", "schema": "", - "name": "anyarray" + "name": "bool" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, "array_dims": 0 - } - ], - "comment": "" - }, - { - "rel": { - "catalog": "pg_catalog", - "schema": "pg_catalog", - "name": "pg_auth_members" - }, - "columns": [ + }, { - "name": "tableoid", + "name": "atthasdef", "not_null": true, "is_array": false, "comment": "", - "length": 4, + "length": 1, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_auth_members" + "name": "pg_attribute" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "oid" + "name": "bool" }, "is_sqlc_slice": false, "embed_table": null, @@ -4038,24 +3970,24 @@ "array_dims": 0 }, { - "name": "cmax", + "name": "atthasmissing", "not_null": true, "is_array": false, "comment": "", - "length": 4, + "length": 1, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_auth_members" + "name": "pg_attribute" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "cid" + "name": "bool" }, "is_sqlc_slice": false, "embed_table": null, @@ -4064,24 +3996,24 @@ "array_dims": 0 }, { - "name": "xmax", + "name": "attidentity", "not_null": true, "is_array": false, "comment": "", - "length": 4, + "length": 1, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_auth_members" + "name": "pg_attribute" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "xid" + "name": "char" }, "is_sqlc_slice": false, "embed_table": null, @@ -4090,24 +4022,24 @@ "array_dims": 0 }, { - "name": "cmin", + "name": "attgenerated", "not_null": true, "is_array": false, "comment": "", - "length": 4, + "length": 1, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_auth_members" + "name": "pg_attribute" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "cid" + "name": "char" }, "is_sqlc_slice": false, "embed_table": null, @@ -4116,24 +4048,24 @@ "array_dims": 0 }, { - "name": "xmin", + "name": "attisdropped", "not_null": true, "is_array": false, "comment": "", - "length": 4, + "length": 1, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_auth_members" + "name": "pg_attribute" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "xid" + "name": "bool" }, "is_sqlc_slice": false, "embed_table": null, @@ -4142,24 +4074,24 @@ "array_dims": 0 }, { - "name": "ctid", + "name": "attislocal", "not_null": true, "is_array": false, "comment": "", - "length": 6, + "length": 1, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_auth_members" + "name": "pg_attribute" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "tid" + "name": "bool" }, "is_sqlc_slice": false, "embed_table": null, @@ -4168,7 +4100,7 @@ "array_dims": 0 }, { - "name": "roleid", + "name": "attinhcount", "not_null": true, "is_array": false, "comment": "", @@ -4179,13 +4111,13 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_auth_members" + "name": "pg_attribute" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "oid" + "name": "int4" }, "is_sqlc_slice": false, "embed_table": null, @@ -4194,7 +4126,7 @@ "array_dims": 0 }, { - "name": "member", + "name": "attcollation", "not_null": true, "is_array": false, "comment": "", @@ -4205,7 +4137,7 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_auth_members" + "name": "pg_attribute" }, "table_alias": "", "type": { @@ -4220,24 +4152,24 @@ "array_dims": 0 }, { - "name": "grantor", - "not_null": true, - "is_array": false, + "name": "attacl", + "not_null": false, + "is_array": true, "comment": "", - "length": 4, + "length": -1, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_auth_members" + "name": "pg_attribute" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "oid" + "name": "_aclitem" }, "is_sqlc_slice": false, "embed_table": null, @@ -4246,24 +4178,76 @@ "array_dims": 0 }, { - "name": "admin_option", - "not_null": true, + "name": "attoptions", + "not_null": false, + "is_array": true, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "pg_catalog", + "schema": "pg_catalog", + "name": "pg_attribute" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "_text" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "", + "unsigned": false, + "array_dims": 0 + }, + { + "name": "attfdwoptions", + "not_null": false, + "is_array": true, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "pg_catalog", + "schema": "pg_catalog", + "name": "pg_attribute" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "_text" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "", + "unsigned": false, + "array_dims": 0 + }, + { + "name": "attmissingval", + "not_null": false, "is_array": false, "comment": "", - "length": 1, + "length": -1, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_auth_members" + "name": "pg_attribute" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "bool" + "name": "anyarray" }, "is_sqlc_slice": false, "embed_table": null, @@ -4278,7 +4262,7 @@ "rel": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_authid" + "name": "pg_auth_members" }, "columns": [ { @@ -4293,7 +4277,7 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_authid" + "name": "pg_auth_members" }, "table_alias": "", "type": { @@ -4319,7 +4303,7 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_authid" + "name": "pg_auth_members" }, "table_alias": "", "type": { @@ -4345,7 +4329,7 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_authid" + "name": "pg_auth_members" }, "table_alias": "", "type": { @@ -4371,7 +4355,7 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_authid" + "name": "pg_auth_members" }, "table_alias": "", "type": { @@ -4397,7 +4381,7 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_authid" + "name": "pg_auth_members" }, "table_alias": "", "type": { @@ -4423,7 +4407,7 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_authid" + "name": "pg_auth_members" }, "table_alias": "", "type": { @@ -4438,7 +4422,121 @@ "array_dims": 0 }, { - "name": "oid", + "name": "roleid", + "not_null": true, + "is_array": false, + "comment": "", + "length": 4, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "pg_catalog", + "schema": "pg_catalog", + "name": "pg_auth_members" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "oid" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "", + "unsigned": false, + "array_dims": 0 + }, + { + "name": "member", + "not_null": true, + "is_array": false, + "comment": "", + "length": 4, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "pg_catalog", + "schema": "pg_catalog", + "name": "pg_auth_members" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "oid" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "", + "unsigned": false, + "array_dims": 0 + }, + { + "name": "grantor", + "not_null": true, + "is_array": false, + "comment": "", + "length": 4, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "pg_catalog", + "schema": "pg_catalog", + "name": "pg_auth_members" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "oid" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "", + "unsigned": false, + "array_dims": 0 + }, + { + "name": "admin_option", + "not_null": true, + "is_array": false, + "comment": "", + "length": 1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "pg_catalog", + "schema": "pg_catalog", + "name": "pg_auth_members" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "bool" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "", + "unsigned": false, + "array_dims": 0 + } + ], + "comment": "" + }, + { + "rel": { + "catalog": "pg_catalog", + "schema": "pg_catalog", + "name": "pg_authid" + }, + "columns": [ + { + "name": "tableoid", "not_null": true, "is_array": false, "comment": "", @@ -4464,11 +4562,11 @@ "array_dims": 0 }, { - "name": "rolname", + "name": "cmax", "not_null": true, "is_array": false, "comment": "", - "length": 64, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -4481,7 +4579,7 @@ "type": { "catalog": "", "schema": "", - "name": "name" + "name": "cid" }, "is_sqlc_slice": false, "embed_table": null, @@ -4490,11 +4588,11 @@ "array_dims": 0 }, { - "name": "rolsuper", + "name": "xmax", "not_null": true, "is_array": false, "comment": "", - "length": 1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -4507,7 +4605,7 @@ "type": { "catalog": "", "schema": "", - "name": "bool" + "name": "xid" }, "is_sqlc_slice": false, "embed_table": null, @@ -4516,11 +4614,11 @@ "array_dims": 0 }, { - "name": "rolinherit", + "name": "cmin", "not_null": true, "is_array": false, "comment": "", - "length": 1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -4533,7 +4631,7 @@ "type": { "catalog": "", "schema": "", - "name": "bool" + "name": "cid" }, "is_sqlc_slice": false, "embed_table": null, @@ -4542,11 +4640,11 @@ "array_dims": 0 }, { - "name": "rolcreaterole", + "name": "xmin", "not_null": true, "is_array": false, "comment": "", - "length": 1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -4559,7 +4657,7 @@ "type": { "catalog": "", "schema": "", - "name": "bool" + "name": "xid" }, "is_sqlc_slice": false, "embed_table": null, @@ -4568,11 +4666,11 @@ "array_dims": 0 }, { - "name": "rolcreatedb", + "name": "ctid", "not_null": true, "is_array": false, "comment": "", - "length": 1, + "length": 6, "is_named_param": false, "is_func_call": false, "scope": "", @@ -4585,7 +4683,7 @@ "type": { "catalog": "", "schema": "", - "name": "bool" + "name": "tid" }, "is_sqlc_slice": false, "embed_table": null, @@ -4594,11 +4692,11 @@ "array_dims": 0 }, { - "name": "rolcanlogin", + "name": "oid", "not_null": true, "is_array": false, "comment": "", - "length": 1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -4611,7 +4709,7 @@ "type": { "catalog": "", "schema": "", - "name": "bool" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, @@ -4620,11 +4718,11 @@ "array_dims": 0 }, { - "name": "rolreplication", + "name": "rolname", "not_null": true, "is_array": false, "comment": "", - "length": 1, + "length": 64, "is_named_param": false, "is_func_call": false, "scope": "", @@ -4637,7 +4735,7 @@ "type": { "catalog": "", "schema": "", - "name": "bool" + "name": "name" }, "is_sqlc_slice": false, "embed_table": null, @@ -4646,7 +4744,7 @@ "array_dims": 0 }, { - "name": "rolbypassrls", + "name": "rolsuper", "not_null": true, "is_array": false, "comment": "", @@ -4672,37 +4770,11 @@ "array_dims": 0 }, { - "name": "rolconnlimit", + "name": "rolinherit", "not_null": true, "is_array": false, "comment": "", - "length": 4, - "is_named_param": false, - "is_func_call": false, - "scope": "", - "table": { - "catalog": "pg_catalog", - "schema": "pg_catalog", - "name": "pg_authid" - }, - "table_alias": "", - "type": { - "catalog": "", - "schema": "", - "name": "int4" - }, - "is_sqlc_slice": false, - "embed_table": null, - "original_name": "", - "unsigned": false, - "array_dims": 0 - }, - { - "name": "rolpassword", - "not_null": false, - "is_array": false, - "comment": "", - "length": -1, + "length": 1, "is_named_param": false, "is_func_call": false, "scope": "", @@ -4715,7 +4787,7 @@ "type": { "catalog": "", "schema": "", - "name": "text" + "name": "bool" }, "is_sqlc_slice": false, "embed_table": null, @@ -4724,11 +4796,11 @@ "array_dims": 0 }, { - "name": "rolvaliduntil", - "not_null": false, + "name": "rolcreaterole", + "not_null": true, "is_array": false, "comment": "", - "length": 8, + "length": 1, "is_named_param": false, "is_func_call": false, "scope": "", @@ -4741,69 +4813,7 @@ "type": { "catalog": "", "schema": "", - "name": "timestamptz" - }, - "is_sqlc_slice": false, - "embed_table": null, - "original_name": "", - "unsigned": false, - "array_dims": 0 - } - ], - "comment": "" - }, - { - "rel": { - "catalog": "pg_catalog", - "schema": "pg_catalog", - "name": "pg_available_extension_versions" - }, - "columns": [ - { - "name": "name", - "not_null": false, - "is_array": false, - "comment": "", - "length": 64, - "is_named_param": false, - "is_func_call": false, - "scope": "", - "table": { - "catalog": "pg_catalog", - "schema": "pg_catalog", - "name": "pg_available_extension_versions" - }, - "table_alias": "", - "type": { - "catalog": "", - "schema": "", - "name": "name" - }, - "is_sqlc_slice": false, - "embed_table": null, - "original_name": "", - "unsigned": false, - "array_dims": 0 - }, - { - "name": "version", - "not_null": false, - "is_array": false, - "comment": "", - "length": -1, - "is_named_param": false, - "is_func_call": false, - "scope": "", - "table": { - "catalog": "pg_catalog", - "schema": "pg_catalog", - "name": "pg_available_extension_versions" - }, - "table_alias": "", - "type": { - "catalog": "", - "schema": "", - "name": "text" + "name": "bool" }, "is_sqlc_slice": false, "embed_table": null, @@ -4812,8 +4822,8 @@ "array_dims": 0 }, { - "name": "installed", - "not_null": false, + "name": "rolcreatedb", + "not_null": true, "is_array": false, "comment": "", "length": 1, @@ -4823,7 +4833,7 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_available_extension_versions" + "name": "pg_authid" }, "table_alias": "", "type": { @@ -4838,8 +4848,8 @@ "array_dims": 0 }, { - "name": "superuser", - "not_null": false, + "name": "rolcanlogin", + "not_null": true, "is_array": false, "comment": "", "length": 1, @@ -4849,7 +4859,7 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_available_extension_versions" + "name": "pg_authid" }, "table_alias": "", "type": { @@ -4864,8 +4874,8 @@ "array_dims": 0 }, { - "name": "trusted", - "not_null": false, + "name": "rolreplication", + "not_null": true, "is_array": false, "comment": "", "length": 1, @@ -4875,7 +4885,7 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_available_extension_versions" + "name": "pg_authid" }, "table_alias": "", "type": { @@ -4890,8 +4900,8 @@ "array_dims": 0 }, { - "name": "relocatable", - "not_null": false, + "name": "rolbypassrls", + "not_null": true, "is_array": false, "comment": "", "length": 1, @@ -4901,7 +4911,7 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_available_extension_versions" + "name": "pg_authid" }, "table_alias": "", "type": { @@ -4916,24 +4926,24 @@ "array_dims": 0 }, { - "name": "schema", - "not_null": false, + "name": "rolconnlimit", + "not_null": true, "is_array": false, "comment": "", - "length": 64, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_available_extension_versions" + "name": "pg_authid" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "name" + "name": "int4" }, "is_sqlc_slice": false, "embed_table": null, @@ -4942,9 +4952,9 @@ "array_dims": 0 }, { - "name": "requires", + "name": "rolpassword", "not_null": false, - "is_array": true, + "is_array": false, "comment": "", "length": -1, "is_named_param": false, @@ -4953,13 +4963,13 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_available_extension_versions" + "name": "pg_authid" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "_name" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, @@ -4968,24 +4978,24 @@ "array_dims": 0 }, { - "name": "comment", + "name": "rolvaliduntil", "not_null": false, "is_array": false, "comment": "", - "length": -1, + "length": 8, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_available_extension_versions" + "name": "pg_authid" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "text" + "name": "timestamptz" }, "is_sqlc_slice": false, "embed_table": null, @@ -5000,7 +5010,7 @@ "rel": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_available_extensions" + "name": "pg_available_extension_versions" }, "columns": [ { @@ -5015,7 +5025,7 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_available_extensions" + "name": "pg_available_extension_versions" }, "table_alias": "", "type": { @@ -5030,7 +5040,7 @@ "array_dims": 0 }, { - "name": "default_version", + "name": "version", "not_null": false, "is_array": false, "comment": "", @@ -5041,7 +5051,7 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_available_extensions" + "name": "pg_available_extension_versions" }, "table_alias": "", "type": { @@ -5056,24 +5066,24 @@ "array_dims": 0 }, { - "name": "installed_version", + "name": "installed", "not_null": false, "is_array": false, "comment": "", - "length": -1, + "length": 1, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_available_extensions" + "name": "pg_available_extension_versions" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "text" + "name": "bool" }, "is_sqlc_slice": false, "embed_table": null, @@ -5082,60 +5092,50 @@ "array_dims": 0 }, { - "name": "comment", + "name": "superuser", "not_null": false, "is_array": false, "comment": "", - "length": -1, + "length": 1, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_available_extensions" + "name": "pg_available_extension_versions" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "text" + "name": "bool" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, "array_dims": 0 - } - ], - "comment": "" - }, - { - "rel": { - "catalog": "pg_catalog", - "schema": "pg_catalog", - "name": "pg_backend_memory_contexts" - }, - "columns": [ + }, { - "name": "name", + "name": "trusted", "not_null": false, "is_array": false, "comment": "", - "length": -1, + "length": 1, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_backend_memory_contexts" + "name": "pg_available_extension_versions" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "text" + "name": "bool" }, "is_sqlc_slice": false, "embed_table": null, @@ -5144,24 +5144,24 @@ "array_dims": 0 }, { - "name": "ident", + "name": "relocatable", "not_null": false, "is_array": false, "comment": "", - "length": -1, + "length": 1, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_backend_memory_contexts" + "name": "pg_available_extension_versions" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "text" + "name": "bool" }, "is_sqlc_slice": false, "embed_table": null, @@ -5170,24 +5170,24 @@ "array_dims": 0 }, { - "name": "parent", + "name": "schema", "not_null": false, "is_array": false, "comment": "", - "length": -1, + "length": 64, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_backend_memory_contexts" + "name": "pg_available_extension_versions" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "text" + "name": "name" }, "is_sqlc_slice": false, "embed_table": null, @@ -5196,24 +5196,24 @@ "array_dims": 0 }, { - "name": "level", + "name": "requires", "not_null": false, - "is_array": false, + "is_array": true, "comment": "", - "length": 4, + "length": -1, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_backend_memory_contexts" + "name": "pg_available_extension_versions" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "int4" + "name": "_name" }, "is_sqlc_slice": false, "embed_table": null, @@ -5222,50 +5222,60 @@ "array_dims": 0 }, { - "name": "total_bytes", + "name": "comment", "not_null": false, "is_array": false, "comment": "", - "length": 8, + "length": -1, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_backend_memory_contexts" + "name": "pg_available_extension_versions" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "int8" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, "array_dims": 0 - }, + } + ], + "comment": "" + }, + { + "rel": { + "catalog": "pg_catalog", + "schema": "pg_catalog", + "name": "pg_available_extensions" + }, + "columns": [ { - "name": "total_nblocks", + "name": "name", "not_null": false, "is_array": false, "comment": "", - "length": 8, + "length": 64, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_backend_memory_contexts" + "name": "pg_available_extensions" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "int8" + "name": "name" }, "is_sqlc_slice": false, "embed_table": null, @@ -5274,24 +5284,24 @@ "array_dims": 0 }, { - "name": "free_bytes", + "name": "default_version", "not_null": false, "is_array": false, "comment": "", - "length": 8, + "length": -1, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_backend_memory_contexts" + "name": "pg_available_extensions" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "int8" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, @@ -5300,24 +5310,24 @@ "array_dims": 0 }, { - "name": "free_chunks", + "name": "installed_version", "not_null": false, "is_array": false, "comment": "", - "length": 8, + "length": -1, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_backend_memory_contexts" + "name": "pg_available_extensions" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "int8" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, @@ -5326,24 +5336,24 @@ "array_dims": 0 }, { - "name": "used_bytes", + "name": "comment", "not_null": false, "is_array": false, "comment": "", - "length": 8, + "length": -1, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_backend_memory_contexts" + "name": "pg_available_extensions" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "int8" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, @@ -5358,28 +5368,28 @@ "rel": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_cast" + "name": "pg_backend_memory_contexts" }, "columns": [ { - "name": "tableoid", - "not_null": true, + "name": "name", + "not_null": false, "is_array": false, "comment": "", - "length": 4, + "length": -1, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_cast" + "name": "pg_backend_memory_contexts" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "oid" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, @@ -5388,24 +5398,24 @@ "array_dims": 0 }, { - "name": "cmax", - "not_null": true, + "name": "ident", + "not_null": false, "is_array": false, "comment": "", - "length": 4, + "length": -1, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_cast" + "name": "pg_backend_memory_contexts" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "cid" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, @@ -5414,24 +5424,24 @@ "array_dims": 0 }, { - "name": "xmax", - "not_null": true, + "name": "parent", + "not_null": false, "is_array": false, "comment": "", - "length": 4, + "length": -1, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_cast" + "name": "pg_backend_memory_contexts" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "xid" + "name": "text" }, "is_sqlc_slice": false, "embed_table": null, @@ -5440,8 +5450,8 @@ "array_dims": 0 }, { - "name": "cmin", - "not_null": true, + "name": "level", + "not_null": false, "is_array": false, "comment": "", "length": 4, @@ -5451,13 +5461,13 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_cast" + "name": "pg_backend_memory_contexts" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "cid" + "name": "int4" }, "is_sqlc_slice": false, "embed_table": null, @@ -5466,24 +5476,24 @@ "array_dims": 0 }, { - "name": "xmin", - "not_null": true, + "name": "total_bytes", + "not_null": false, "is_array": false, "comment": "", - "length": 4, + "length": 8, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_cast" + "name": "pg_backend_memory_contexts" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "xid" + "name": "int8" }, "is_sqlc_slice": false, "embed_table": null, @@ -5492,24 +5502,24 @@ "array_dims": 0 }, { - "name": "ctid", - "not_null": true, + "name": "total_nblocks", + "not_null": false, "is_array": false, "comment": "", - "length": 6, + "length": 8, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_cast" + "name": "pg_backend_memory_contexts" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "tid" + "name": "int8" }, "is_sqlc_slice": false, "embed_table": null, @@ -5518,24 +5528,24 @@ "array_dims": 0 }, { - "name": "oid", - "not_null": true, + "name": "free_bytes", + "not_null": false, "is_array": false, "comment": "", - "length": 4, + "length": 8, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_cast" + "name": "pg_backend_memory_contexts" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "oid" + "name": "int8" }, "is_sqlc_slice": false, "embed_table": null, @@ -5544,24 +5554,24 @@ "array_dims": 0 }, { - "name": "castsource", - "not_null": true, + "name": "free_chunks", + "not_null": false, "is_array": false, "comment": "", - "length": 4, + "length": 8, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_cast" + "name": "pg_backend_memory_contexts" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "oid" + "name": "int8" }, "is_sqlc_slice": false, "embed_table": null, @@ -5570,33 +5580,43 @@ "array_dims": 0 }, { - "name": "casttarget", - "not_null": true, + "name": "used_bytes", + "not_null": false, "is_array": false, "comment": "", - "length": 4, + "length": 8, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_cast" + "name": "pg_backend_memory_contexts" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "oid" + "name": "int8" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, "array_dims": 0 - }, + } + ], + "comment": "" + }, + { + "rel": { + "catalog": "pg_catalog", + "schema": "pg_catalog", + "name": "pg_cast" + }, + "columns": [ { - "name": "castfunc", + "name": "tableoid", "not_null": true, "is_array": false, "comment": "", @@ -5622,11 +5642,11 @@ "array_dims": 0 }, { - "name": "castcontext", + "name": "cmax", "not_null": true, "is_array": false, "comment": "", - "length": 1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -5639,7 +5659,7 @@ "type": { "catalog": "", "schema": "", - "name": "char" + "name": "cid" }, "is_sqlc_slice": false, "embed_table": null, @@ -5648,11 +5668,11 @@ "array_dims": 0 }, { - "name": "castmethod", + "name": "xmax", "not_null": true, "is_array": false, "comment": "", - "length": 1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -5665,26 +5685,16 @@ "type": { "catalog": "", "schema": "", - "name": "char" + "name": "xid" }, "is_sqlc_slice": false, "embed_table": null, "original_name": "", "unsigned": false, "array_dims": 0 - } - ], - "comment": "" - }, - { - "rel": { - "catalog": "pg_catalog", - "schema": "pg_catalog", - "name": "pg_class" - }, - "columns": [ + }, { - "name": "tableoid", + "name": "cmin", "not_null": true, "is_array": false, "comment": "", @@ -5695,13 +5705,13 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_class" + "name": "pg_cast" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "oid" + "name": "cid" }, "is_sqlc_slice": false, "embed_table": null, @@ -5710,7 +5720,7 @@ "array_dims": 0 }, { - "name": "cmax", + "name": "xmin", "not_null": true, "is_array": false, "comment": "", @@ -5721,13 +5731,13 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_class" + "name": "pg_cast" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "cid" + "name": "xid" }, "is_sqlc_slice": false, "embed_table": null, @@ -5736,7 +5746,33 @@ "array_dims": 0 }, { - "name": "xmax", + "name": "ctid", + "not_null": true, + "is_array": false, + "comment": "", + "length": 6, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "pg_catalog", + "schema": "pg_catalog", + "name": "pg_cast" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "tid" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "", + "unsigned": false, + "array_dims": 0 + }, + { + "name": "oid", "not_null": true, "is_array": false, "comment": "", @@ -5747,13 +5783,13 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_class" + "name": "pg_cast" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "xid" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, @@ -5762,7 +5798,7 @@ "array_dims": 0 }, { - "name": "cmin", + "name": "castsource", "not_null": true, "is_array": false, "comment": "", @@ -5773,13 +5809,13 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_class" + "name": "pg_cast" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "cid" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, @@ -5788,7 +5824,7 @@ "array_dims": 0 }, { - "name": "xmin", + "name": "casttarget", "not_null": true, "is_array": false, "comment": "", @@ -5799,13 +5835,13 @@ "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_class" + "name": "pg_cast" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "xid" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, @@ -5814,24 +5850,24 @@ "array_dims": 0 }, { - "name": "ctid", + "name": "castfunc", "not_null": true, "is_array": false, "comment": "", - "length": 6, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", "table": { "catalog": "pg_catalog", "schema": "pg_catalog", - "name": "pg_class" + "name": "pg_cast" }, "table_alias": "", "type": { "catalog": "", "schema": "", - "name": "tid" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, @@ -5840,7 +5876,69 @@ "array_dims": 0 }, { - "name": "oid", + "name": "castcontext", + "not_null": true, + "is_array": false, + "comment": "", + "length": 1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "pg_catalog", + "schema": "pg_catalog", + "name": "pg_cast" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "char" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "", + "unsigned": false, + "array_dims": 0 + }, + { + "name": "castmethod", + "not_null": true, + "is_array": false, + "comment": "", + "length": 1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "pg_catalog", + "schema": "pg_catalog", + "name": "pg_cast" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "char" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "", + "unsigned": false, + "array_dims": 0 + } + ], + "comment": "" + }, + { + "rel": { + "catalog": "pg_catalog", + "schema": "pg_catalog", + "name": "pg_class" + }, + "columns": [ + { + "name": "tableoid", "not_null": true, "is_array": false, "comment": "", @@ -5866,11 +5964,11 @@ "array_dims": 0 }, { - "name": "relname", + "name": "cmax", "not_null": true, "is_array": false, "comment": "", - "length": 64, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -5883,7 +5981,7 @@ "type": { "catalog": "", "schema": "", - "name": "name" + "name": "cid" }, "is_sqlc_slice": false, "embed_table": null, @@ -5892,7 +5990,7 @@ "array_dims": 0 }, { - "name": "relnamespace", + "name": "xmax", "not_null": true, "is_array": false, "comment": "", @@ -5909,7 +6007,7 @@ "type": { "catalog": "", "schema": "", - "name": "oid" + "name": "xid" }, "is_sqlc_slice": false, "embed_table": null, @@ -5918,7 +6016,7 @@ "array_dims": 0 }, { - "name": "reltype", + "name": "cmin", "not_null": true, "is_array": false, "comment": "", @@ -5935,7 +6033,7 @@ "type": { "catalog": "", "schema": "", - "name": "oid" + "name": "cid" }, "is_sqlc_slice": false, "embed_table": null, @@ -5944,7 +6042,7 @@ "array_dims": 0 }, { - "name": "reloftype", + "name": "xmin", "not_null": true, "is_array": false, "comment": "", @@ -5961,7 +6059,7 @@ "type": { "catalog": "", "schema": "", - "name": "oid" + "name": "xid" }, "is_sqlc_slice": false, "embed_table": null, @@ -5970,11 +6068,11 @@ "array_dims": 0 }, { - "name": "relowner", + "name": "ctid", "not_null": true, "is_array": false, "comment": "", - "length": 4, + "length": 6, "is_named_param": false, "is_func_call": false, "scope": "", @@ -5987,7 +6085,7 @@ "type": { "catalog": "", "schema": "", - "name": "oid" + "name": "tid" }, "is_sqlc_slice": false, "embed_table": null, @@ -5996,7 +6094,7 @@ "array_dims": 0 }, { - "name": "relam", + "name": "oid", "not_null": true, "is_array": false, "comment": "", @@ -6022,7 +6120,33 @@ "array_dims": 0 }, { - "name": "relfilenode", + "name": "relname", + "not_null": true, + "is_array": false, + "comment": "", + "length": 64, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "pg_catalog", + "schema": "pg_catalog", + "name": "pg_class" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "name" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "", + "unsigned": false, + "array_dims": 0 + }, + { + "name": "relnamespace", "not_null": true, "is_array": false, "comment": "", @@ -6048,7 +6172,7 @@ "array_dims": 0 }, { - "name": "reltablespace", + "name": "reltype", "not_null": true, "is_array": false, "comment": "", @@ -6074,7 +6198,7 @@ "array_dims": 0 }, { - "name": "relpages", + "name": "reloftype", "not_null": true, "is_array": false, "comment": "", @@ -6091,7 +6215,7 @@ "type": { "catalog": "", "schema": "", - "name": "int4" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, @@ -6100,7 +6224,7 @@ "array_dims": 0 }, { - "name": "reltuples", + "name": "relowner", "not_null": true, "is_array": false, "comment": "", @@ -6117,7 +6241,7 @@ "type": { "catalog": "", "schema": "", - "name": "float4" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, @@ -6126,7 +6250,7 @@ "array_dims": 0 }, { - "name": "relallvisible", + "name": "relam", "not_null": true, "is_array": false, "comment": "", @@ -6143,7 +6267,7 @@ "type": { "catalog": "", "schema": "", - "name": "int4" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, @@ -6152,7 +6276,7 @@ "array_dims": 0 }, { - "name": "reltoastrelid", + "name": "relfilenode", "not_null": true, "is_array": false, "comment": "", @@ -6178,11 +6302,11 @@ "array_dims": 0 }, { - "name": "relhasindex", + "name": "reltablespace", "not_null": true, "is_array": false, "comment": "", - "length": 1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -6195,7 +6319,7 @@ "type": { "catalog": "", "schema": "", - "name": "bool" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, @@ -6204,11 +6328,11 @@ "array_dims": 0 }, { - "name": "relisshared", + "name": "relpages", "not_null": true, "is_array": false, "comment": "", - "length": 1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -6221,7 +6345,7 @@ "type": { "catalog": "", "schema": "", - "name": "bool" + "name": "int4" }, "is_sqlc_slice": false, "embed_table": null, @@ -6230,11 +6354,11 @@ "array_dims": 0 }, { - "name": "relpersistence", + "name": "reltuples", "not_null": true, "is_array": false, "comment": "", - "length": 1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -6247,7 +6371,7 @@ "type": { "catalog": "", "schema": "", - "name": "char" + "name": "float4" }, "is_sqlc_slice": false, "embed_table": null, @@ -6256,11 +6380,11 @@ "array_dims": 0 }, { - "name": "relkind", + "name": "relallvisible", "not_null": true, "is_array": false, "comment": "", - "length": 1, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -6273,7 +6397,7 @@ "type": { "catalog": "", "schema": "", - "name": "char" + "name": "int4" }, "is_sqlc_slice": false, "embed_table": null, @@ -6282,11 +6406,11 @@ "array_dims": 0 }, { - "name": "relnatts", + "name": "reltoastrelid", "not_null": true, "is_array": false, "comment": "", - "length": 2, + "length": 4, "is_named_param": false, "is_func_call": false, "scope": "", @@ -6299,7 +6423,7 @@ "type": { "catalog": "", "schema": "", - "name": "int2" + "name": "oid" }, "is_sqlc_slice": false, "embed_table": null, @@ -6308,11 +6432,11 @@ "array_dims": 0 }, { - "name": "relchecks", + "name": "relhasindex", "not_null": true, "is_array": false, "comment": "", - "length": 2, + "length": 1, "is_named_param": false, "is_func_call": false, "scope": "", @@ -6325,7 +6449,7 @@ "type": { "catalog": "", "schema": "", - "name": "int2" + "name": "bool" }, "is_sqlc_slice": false, "embed_table": null, @@ -6334,7 +6458,137 @@ "array_dims": 0 }, { - "name": "relhasrules", + "name": "relisshared", + "not_null": true, + "is_array": false, + "comment": "", + "length": 1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "pg_catalog", + "schema": "pg_catalog", + "name": "pg_class" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "bool" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "", + "unsigned": false, + "array_dims": 0 + }, + { + "name": "relpersistence", + "not_null": true, + "is_array": false, + "comment": "", + "length": 1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "pg_catalog", + "schema": "pg_catalog", + "name": "pg_class" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "char" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "", + "unsigned": false, + "array_dims": 0 + }, + { + "name": "relkind", + "not_null": true, + "is_array": false, + "comment": "", + "length": 1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "pg_catalog", + "schema": "pg_catalog", + "name": "pg_class" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "char" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "", + "unsigned": false, + "array_dims": 0 + }, + { + "name": "relnatts", + "not_null": true, + "is_array": false, + "comment": "", + "length": 2, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "pg_catalog", + "schema": "pg_catalog", + "name": "pg_class" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "int2" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "", + "unsigned": false, + "array_dims": 0 + }, + { + "name": "relchecks", + "not_null": true, + "is_array": false, + "comment": "", + "length": 2, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "pg_catalog", + "schema": "pg_catalog", + "name": "pg_class" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "int2" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "", + "unsigned": false, + "array_dims": 0 + }, + { + "name": "relhasrules", "not_null": true, "is_array": false, "comment": "", @@ -72561,6 +72815,1264 @@ "comments": [], "filename": "queries.sql", "insert_into_table": null + }, + { + "text": "\nINSERT INTO links (endpoint_url, dest_url, oid)\nVALUES ($1, $2, $3)\nRETURNING lid, endpoint_url, dest_url, oid, created_at", + "name": "CreateLink", + "cmd": ":one", + "columns": [ + { + "name": "lid", + "not_null": true, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "", + "name": "links" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "uuid" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "lid", + "unsigned": false, + "array_dims": 0 + }, + { + "name": "endpoint_url", + "not_null": true, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "", + "name": "links" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "text" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "endpoint_url", + "unsigned": false, + "array_dims": 0 + }, + { + "name": "dest_url", + "not_null": true, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "", + "name": "links" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "text" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "dest_url", + "unsigned": false, + "array_dims": 0 + }, + { + "name": "oid", + "not_null": true, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "", + "name": "links" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "uuid" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "oid", + "unsigned": false, + "array_dims": 0 + }, + { + "name": "created_at", + "not_null": false, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "", + "name": "links" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "pg_catalog", + "name": "timestamp" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "created_at", + "unsigned": false, + "array_dims": 0 + } + ], + "params": [ + { + "number": 1, + "column": { + "name": "endpoint_url", + "not_null": true, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "public", + "name": "links" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "text" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "endpoint_url", + "unsigned": false, + "array_dims": 0 + } + }, + { + "number": 2, + "column": { + "name": "dest_url", + "not_null": true, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "public", + "name": "links" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "text" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "dest_url", + "unsigned": false, + "array_dims": 0 + } + }, + { + "number": 3, + "column": { + "name": "oid", + "not_null": true, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "public", + "name": "links" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "uuid" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "oid", + "unsigned": false, + "array_dims": 0 + } + } + ], + "comments": [ + " Link Queries" + ], + "filename": "queries.sql", + "insert_into_table": { + "catalog": "", + "schema": "", + "name": "links" + } + }, + { + "text": "SELECT lid, endpoint_url, dest_url, oid, created_at FROM links WHERE lid = $1", + "name": "GetLinkByLID", + "cmd": ":one", + "columns": [ + { + "name": "lid", + "not_null": true, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "", + "name": "links" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "uuid" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "lid", + "unsigned": false, + "array_dims": 0 + }, + { + "name": "endpoint_url", + "not_null": true, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "", + "name": "links" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "text" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "endpoint_url", + "unsigned": false, + "array_dims": 0 + }, + { + "name": "dest_url", + "not_null": true, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "", + "name": "links" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "text" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "dest_url", + "unsigned": false, + "array_dims": 0 + }, + { + "name": "oid", + "not_null": true, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "", + "name": "links" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "uuid" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "oid", + "unsigned": false, + "array_dims": 0 + }, + { + "name": "created_at", + "not_null": false, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "", + "name": "links" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "pg_catalog", + "name": "timestamp" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "created_at", + "unsigned": false, + "array_dims": 0 + } + ], + "params": [ + { + "number": 1, + "column": { + "name": "lid", + "not_null": true, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "", + "name": "links" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "uuid" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "lid", + "unsigned": false, + "array_dims": 0 + } + } + ], + "comments": [], + "filename": "queries.sql", + "insert_into_table": null + }, + { + "text": "SELECT lid, endpoint_url, dest_url, oid, created_at FROM links WHERE endpoint_url = $1", + "name": "GetLinkByEndpointURL", + "cmd": ":one", + "columns": [ + { + "name": "lid", + "not_null": true, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "", + "name": "links" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "uuid" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "lid", + "unsigned": false, + "array_dims": 0 + }, + { + "name": "endpoint_url", + "not_null": true, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "", + "name": "links" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "text" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "endpoint_url", + "unsigned": false, + "array_dims": 0 + }, + { + "name": "dest_url", + "not_null": true, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "", + "name": "links" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "text" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "dest_url", + "unsigned": false, + "array_dims": 0 + }, + { + "name": "oid", + "not_null": true, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "", + "name": "links" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "uuid" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "oid", + "unsigned": false, + "array_dims": 0 + }, + { + "name": "created_at", + "not_null": false, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "", + "name": "links" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "pg_catalog", + "name": "timestamp" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "created_at", + "unsigned": false, + "array_dims": 0 + } + ], + "params": [ + { + "number": 1, + "column": { + "name": "endpoint_url", + "not_null": true, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "", + "name": "links" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "text" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "endpoint_url", + "unsigned": false, + "array_dims": 0 + } + } + ], + "comments": [], + "filename": "queries.sql", + "insert_into_table": null + }, + { + "text": "UPDATE links\nSET endpoint_url = COALESCE($2, endpoint_url),\n dest_url = COALESCE($3, dest_url)\nWHERE lid = $1\nRETURNING lid, endpoint_url, dest_url, oid, created_at", + "name": "UpdateLink", + "cmd": ":one", + "columns": [ + { + "name": "lid", + "not_null": true, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "", + "name": "links" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "uuid" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "lid", + "unsigned": false, + "array_dims": 0 + }, + { + "name": "endpoint_url", + "not_null": true, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "", + "name": "links" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "text" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "endpoint_url", + "unsigned": false, + "array_dims": 0 + }, + { + "name": "dest_url", + "not_null": true, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "", + "name": "links" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "text" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "dest_url", + "unsigned": false, + "array_dims": 0 + }, + { + "name": "oid", + "not_null": true, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "", + "name": "links" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "uuid" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "oid", + "unsigned": false, + "array_dims": 0 + }, + { + "name": "created_at", + "not_null": false, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "", + "name": "links" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "pg_catalog", + "name": "timestamp" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "created_at", + "unsigned": false, + "array_dims": 0 + } + ], + "params": [ + { + "number": 1, + "column": { + "name": "lid", + "not_null": true, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "", + "name": "links" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "uuid" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "lid", + "unsigned": false, + "array_dims": 0 + } + }, + { + "number": 2, + "column": { + "name": "endpoint_url", + "not_null": false, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": true, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "public", + "name": "links" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "text" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "endpoint_url", + "unsigned": false, + "array_dims": 0 + } + }, + { + "number": 3, + "column": { + "name": "dest_url", + "not_null": false, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": true, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "public", + "name": "links" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "text" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "dest_url", + "unsigned": false, + "array_dims": 0 + } + } + ], + "comments": [], + "filename": "queries.sql", + "insert_into_table": null + }, + { + "text": "DELETE FROM links WHERE lid = $1", + "name": "DeleteLink", + "cmd": ":exec", + "columns": [], + "params": [ + { + "number": 1, + "column": { + "name": "lid", + "not_null": true, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "", + "name": "links" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "uuid" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "lid", + "unsigned": false, + "array_dims": 0 + } + } + ], + "comments": [], + "filename": "queries.sql", + "insert_into_table": null + }, + { + "text": "SELECT lid, endpoint_url, dest_url, oid, created_at FROM links WHERE oid = $1 ORDER BY created_at DESC", + "name": "ListLinksByOrg", + "cmd": ":many", + "columns": [ + { + "name": "lid", + "not_null": true, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "", + "name": "links" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "uuid" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "lid", + "unsigned": false, + "array_dims": 0 + }, + { + "name": "endpoint_url", + "not_null": true, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "", + "name": "links" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "text" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "endpoint_url", + "unsigned": false, + "array_dims": 0 + }, + { + "name": "dest_url", + "not_null": true, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "", + "name": "links" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "text" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "dest_url", + "unsigned": false, + "array_dims": 0 + }, + { + "name": "oid", + "not_null": true, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "", + "name": "links" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "uuid" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "oid", + "unsigned": false, + "array_dims": 0 + }, + { + "name": "created_at", + "not_null": false, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "", + "name": "links" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "pg_catalog", + "name": "timestamp" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "created_at", + "unsigned": false, + "array_dims": 0 + } + ], + "params": [ + { + "number": 1, + "column": { + "name": "oid", + "not_null": true, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "", + "name": "links" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "uuid" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "oid", + "unsigned": false, + "array_dims": 0 + } + } + ], + "comments": [], + "filename": "queries.sql", + "insert_into_table": null + }, + { + "text": "INSERT INTO link_visits (lid, uid)\nVALUES ($1, $2)\nRETURNING lvid, lid, uid, created_at", + "name": "LogLinkVisit", + "cmd": ":one", + "columns": [ + { + "name": "lvid", + "not_null": true, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "", + "name": "link_visits" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "uuid" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "lvid", + "unsigned": false, + "array_dims": 0 + }, + { + "name": "lid", + "not_null": true, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "", + "name": "link_visits" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "uuid" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "lid", + "unsigned": false, + "array_dims": 0 + }, + { + "name": "uid", + "not_null": false, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "", + "name": "link_visits" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "uuid" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "uid", + "unsigned": false, + "array_dims": 0 + }, + { + "name": "created_at", + "not_null": false, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "", + "name": "link_visits" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "pg_catalog", + "name": "timestamp" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "created_at", + "unsigned": false, + "array_dims": 0 + } + ], + "params": [ + { + "number": 1, + "column": { + "name": "lid", + "not_null": true, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "public", + "name": "link_visits" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "uuid" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "lid", + "unsigned": false, + "array_dims": 0 + } + }, + { + "number": 2, + "column": { + "name": "uid", + "not_null": false, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "public", + "name": "link_visits" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "uuid" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "uid", + "unsigned": false, + "array_dims": 0 + } + } + ], + "comments": [], + "filename": "queries.sql", + "insert_into_table": { + "catalog": "", + "schema": "", + "name": "link_visits" + } + }, + { + "text": "SELECT COUNT(*) FROM link_visits WHERE lid = $1", + "name": "GetTotalVisits", + "cmd": ":one", + "columns": [ + { + "name": "count", + "not_null": true, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": true, + "scope": "", + "table": null, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "bigint" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "", + "unsigned": false, + "array_dims": 0 + } + ], + "params": [ + { + "number": 1, + "column": { + "name": "lid", + "not_null": true, + "is_array": false, + "comment": "", + "length": -1, + "is_named_param": false, + "is_func_call": false, + "scope": "", + "table": { + "catalog": "", + "schema": "", + "name": "link_visits" + }, + "table_alias": "", + "type": { + "catalog": "", + "schema": "", + "name": "uuid" + }, + "is_sqlc_slice": false, + "embed_table": null, + "original_name": "lid", + "unsigned": false, + "array_dims": 0 + } + } + ], + "comments": [], + "filename": "queries.sql", + "insert_into_table": null } ], "sqlc_version": "v1.30.0", diff --git a/go.mod b/go.mod index 1473260..c502022 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,8 @@ require ( github.com/swaggo/swag v1.16.6 github.com/testcontainers/testcontainers-go v0.40.0 github.com/testcontainers/testcontainers-go/modules/postgres v0.40.0 + github.com/yeqown/go-qrcode/v2 v2.2.5 + github.com/yeqown/go-qrcode/writer/standard v1.3.0 golang.org/x/crypto v0.47.0 golang.org/x/oauth2 v0.34.0 ) @@ -38,6 +40,7 @@ require ( github.com/docker/go-units v0.5.0 // indirect github.com/ebitengine/purego v0.8.4 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fogleman/gg v1.3.0 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect @@ -45,6 +48,7 @@ require ( github.com/go-openapi/jsonreference v0.20.0 // indirect github.com/go-openapi/spec v0.20.6 // indirect github.com/go-openapi/swag v0.19.15 // indirect + github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect @@ -73,6 +77,7 @@ require ( github.com/swaggo/files/v2 v2.0.0 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect + github.com/yeqown/reedsolomon v1.0.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect @@ -80,6 +85,7 @@ require ( go.opentelemetry.io/otel/metric v1.38.0 // indirect go.opentelemetry.io/otel/sdk v1.38.0 // indirect go.opentelemetry.io/otel/trace v1.38.0 // indirect + golang.org/x/image v0.10.0 // indirect golang.org/x/mod v0.31.0 // indirect golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.40.0 // indirect diff --git a/go.sum b/go.sum index 28cb530..08e6c84 100644 --- a/go.sum +++ b/go.sum @@ -43,6 +43,8 @@ github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0o github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= +github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/go-chi/chi/v5 v5.2.4 h1:WtFKPHwlywe8Srng8j2BhOD9312j9cGUxG1SP4V2cR4= github.com/go-chi/chi/v5 v5.2.4/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -64,6 +66,8 @@ github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyr github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= @@ -164,6 +168,13 @@ github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFA github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/yeqown/go-qrcode/v2 v2.2.5 h1:HCOe2bSjkhZyYoyyNaXNzh4DJZll6inVJQQw+8228Zk= +github.com/yeqown/go-qrcode/v2 v2.2.5/go.mod h1:uHpt9CM0V1HeXLz+Wg5MN50/sI/fQhfkZlOM+cOTHxw= +github.com/yeqown/go-qrcode/writer/standard v1.3.0 h1:chdyhEfRtUPgQtuPeaWVGQ/TQx4rE1PqeoW3U+53t34= +github.com/yeqown/go-qrcode/writer/standard v1.3.0/go.mod h1:O4MbzsotGCvy8upYPCR91j81dr5XLT7heuljcNXW+oQ= +github.com/yeqown/reedsolomon v1.0.0 h1:x1h/Ej/uJnNu8jaX7GLHBWmZKCAWjEJTetkqaabr4B0= +github.com/yeqown/reedsolomon v1.0.0/go.mod h1:P76zpcn2TCuL0ul1Fso373qHRc69LKwAw/Iy6g1WiiM= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= @@ -184,32 +195,64 @@ go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJr go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= +golang.org/x/image v0.10.0 h1:gXjUUtwtx5yOE0VKWq1CH4IJAClq4UGgUA3i+rpON9M= +golang.org/x/image v0.10.0/go.mod h1:jtrku+n79PfroUbvDdeUWMAI+heR786BofxrbiSF+J0= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI= golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY= golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA= golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M= google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I= diff --git a/internal/database/mocks/Querier.go b/internal/database/mocks/Querier.go index f8822b0..6ac680c 100644 --- a/internal/database/mocks/Querier.go +++ b/internal/database/mocks/Querier.go @@ -110,6 +110,34 @@ func (_m *Querier) CreateEvent(ctx context.Context, arg database.CreateEventPara return r0, r1 } +// CreateLink provides a mock function with given fields: ctx, arg +func (_m *Querier) CreateLink(ctx context.Context, arg database.CreateLinkParams) (database.Link, error) { + ret := _m.Called(ctx, arg) + + if len(ret) == 0 { + panic("no return value specified for CreateLink") + } + + var r0 database.Link + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, database.CreateLinkParams) (database.Link, error)); ok { + return rf(ctx, arg) + } + if rf, ok := ret.Get(0).(func(context.Context, database.CreateLinkParams) database.Link); ok { + r0 = rf(ctx, arg) + } else { + r0 = ret.Get(0).(database.Link) + } + + if rf, ok := ret.Get(1).(func(context.Context, database.CreateLinkParams) error); ok { + r1 = rf(ctx, arg) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // CreateOrganization provides a mock function with given fields: ctx, name func (_m *Querier) CreateOrganization(ctx context.Context, name string) (database.Organization, error) { ret := _m.Called(ctx, name) @@ -184,6 +212,24 @@ func (_m *Querier) DeleteEvent(ctx context.Context, eid uuid.UUID) error { return r0 } +// DeleteLink provides a mock function with given fields: ctx, lid +func (_m *Querier) DeleteLink(ctx context.Context, lid uuid.UUID) error { + ret := _m.Called(ctx, lid) + + if len(ret) == 0 { + panic("no return value specified for DeleteLink") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, uuid.UUID) error); ok { + r0 = rf(ctx, lid) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // DeleteOrganization provides a mock function with given fields: ctx, oid func (_m *Querier) DeleteOrganization(ctx context.Context, oid uuid.UUID) error { ret := _m.Called(ctx, oid) @@ -306,6 +352,62 @@ func (_m *Querier) GetEventRegistrations(ctx context.Context, eid uuid.UUID) ([] return r0, r1 } +// GetLinkByEndpointURL provides a mock function with given fields: ctx, endpointUrl +func (_m *Querier) GetLinkByEndpointURL(ctx context.Context, endpointUrl string) (database.Link, error) { + ret := _m.Called(ctx, endpointUrl) + + if len(ret) == 0 { + panic("no return value specified for GetLinkByEndpointURL") + } + + var r0 database.Link + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) (database.Link, error)); ok { + return rf(ctx, endpointUrl) + } + if rf, ok := ret.Get(0).(func(context.Context, string) database.Link); ok { + r0 = rf(ctx, endpointUrl) + } else { + r0 = ret.Get(0).(database.Link) + } + + if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = rf(ctx, endpointUrl) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetLinkByLID provides a mock function with given fields: ctx, lid +func (_m *Querier) GetLinkByLID(ctx context.Context, lid uuid.UUID) (database.Link, error) { + ret := _m.Called(ctx, lid) + + if len(ret) == 0 { + panic("no return value specified for GetLinkByLID") + } + + var r0 database.Link + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, uuid.UUID) (database.Link, error)); ok { + return rf(ctx, lid) + } + if rf, ok := ret.Get(0).(func(context.Context, uuid.UUID) database.Link); ok { + r0 = rf(ctx, lid) + } else { + r0 = ret.Get(0).(database.Link) + } + + if rf, ok := ret.Get(1).(func(context.Context, uuid.UUID) error); ok { + r1 = rf(ctx, lid) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetOrgMembers provides a mock function with given fields: ctx, oid func (_m *Querier) GetOrgMembers(ctx context.Context, oid uuid.UUID) ([]database.GetOrgMembersRow, error) { ret := _m.Called(ctx, oid) @@ -364,6 +466,34 @@ func (_m *Querier) GetOrganizationByID(ctx context.Context, oid uuid.UUID) (data return r0, r1 } +// GetTotalVisits provides a mock function with given fields: ctx, lid +func (_m *Querier) GetTotalVisits(ctx context.Context, lid uuid.UUID) (int64, error) { + ret := _m.Called(ctx, lid) + + if len(ret) == 0 { + panic("no return value specified for GetTotalVisits") + } + + var r0 int64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, uuid.UUID) (int64, error)); ok { + return rf(ctx, lid) + } + if rf, ok := ret.Get(0).(func(context.Context, uuid.UUID) int64); ok { + r0 = rf(ctx, lid) + } else { + r0 = ret.Get(0).(int64) + } + + if rf, ok := ret.Get(1).(func(context.Context, uuid.UUID) error); ok { + r1 = rf(ctx, lid) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetUserByEmail provides a mock function with given fields: ctx, personalEmail func (_m *Querier) GetUserByEmail(ctx context.Context, personalEmail pgtype.Text) (database.User, error) { ret := _m.Called(ctx, personalEmail) @@ -626,6 +756,36 @@ func (_m *Querier) ListEventsByOrg(ctx context.Context, arg database.ListEventsB return r0, r1 } +// ListLinksByOrg provides a mock function with given fields: ctx, oid +func (_m *Querier) ListLinksByOrg(ctx context.Context, oid uuid.UUID) ([]database.Link, error) { + ret := _m.Called(ctx, oid) + + if len(ret) == 0 { + panic("no return value specified for ListLinksByOrg") + } + + var r0 []database.Link + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, uuid.UUID) ([]database.Link, error)); ok { + return rf(ctx, oid) + } + if rf, ok := ret.Get(0).(func(context.Context, uuid.UUID) []database.Link); ok { + r0 = rf(ctx, oid) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]database.Link) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, uuid.UUID) error); ok { + r1 = rf(ctx, oid) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // ListOrganizations provides a mock function with given fields: ctx, arg func (_m *Querier) ListOrganizations(ctx context.Context, arg database.ListOrganizationsParams) ([]database.Organization, error) { ret := _m.Called(ctx, arg) @@ -686,6 +846,34 @@ func (_m *Querier) ListUsers(ctx context.Context, arg database.ListUsersParams) return r0, r1 } +// LogLinkVisit provides a mock function with given fields: ctx, arg +func (_m *Querier) LogLinkVisit(ctx context.Context, arg database.LogLinkVisitParams) (database.LinkVisit, error) { + ret := _m.Called(ctx, arg) + + if len(ret) == 0 { + panic("no return value specified for LogLinkVisit") + } + + var r0 database.LinkVisit + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, database.LogLinkVisitParams) (database.LinkVisit, error)); ok { + return rf(ctx, arg) + } + if rf, ok := ret.Get(0).(func(context.Context, database.LogLinkVisitParams) database.LinkVisit); ok { + r0 = rf(ctx, arg) + } else { + r0 = ret.Get(0).(database.LinkVisit) + } + + if rf, ok := ret.Get(1).(func(context.Context, database.LogLinkVisitParams) error); ok { + r1 = rf(ctx, arg) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // RegisterForEvent provides a mock function with given fields: ctx, arg func (_m *Querier) RegisterForEvent(ctx context.Context, arg database.RegisterForEventParams) error { ret := _m.Called(ctx, arg) @@ -804,6 +992,34 @@ func (_m *Querier) UpdateEvent(ctx context.Context, arg database.UpdateEventPara return r0, r1 } +// UpdateLink provides a mock function with given fields: ctx, arg +func (_m *Querier) UpdateLink(ctx context.Context, arg database.UpdateLinkParams) (database.Link, error) { + ret := _m.Called(ctx, arg) + + if len(ret) == 0 { + panic("no return value specified for UpdateLink") + } + + var r0 database.Link + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, database.UpdateLinkParams) (database.Link, error)); ok { + return rf(ctx, arg) + } + if rf, ok := ret.Get(0).(func(context.Context, database.UpdateLinkParams) database.Link); ok { + r0 = rf(ctx, arg) + } else { + r0 = ret.Get(0).(database.Link) + } + + if rf, ok := ret.Get(1).(func(context.Context, database.UpdateLinkParams) error); ok { + r1 = rf(ctx, arg) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // UpdateOrganization provides a mock function with given fields: ctx, arg func (_m *Querier) UpdateOrganization(ctx context.Context, arg database.UpdateOrganizationParams) (database.Organization, error) { ret := _m.Called(ctx, arg) diff --git a/internal/database/models.go b/internal/database/models.go index a54ed5a..df83674 100644 --- a/internal/database/models.go +++ b/internal/database/models.go @@ -89,6 +89,21 @@ type EventRegistration struct { DateRegistered pgtype.Date `json:"date_registered"` } +type Link struct { + Lid uuid.UUID `json:"lid"` + EndpointUrl string `json:"endpoint_url"` + DestUrl string `json:"dest_url"` + Oid uuid.UUID `json:"oid"` + CreatedAt pgtype.Timestamp `json:"created_at"` +} + +type LinkVisit struct { + Lvid uuid.UUID `json:"lvid"` + Lid uuid.UUID `json:"lid"` + Uid pgtype.UUID `json:"uid"` + CreatedAt pgtype.Timestamp `json:"created_at"` +} + type OrgMember struct { Uid uuid.UUID `json:"uid"` Oid uuid.UUID `json:"oid"` diff --git a/internal/database/querier.go b/internal/database/querier.go index fa8a0c9..bd8beac 100644 --- a/internal/database/querier.go +++ b/internal/database/querier.go @@ -16,17 +16,23 @@ type Querier interface { AddOrgMember(ctx context.Context, arg AddOrgMemberParams) error CreateBotToken(ctx context.Context, arg CreateBotTokenParams) (BotToken, error) CreateEvent(ctx context.Context, arg CreateEventParams) (Event, error) + // Link Queries + CreateLink(ctx context.Context, arg CreateLinkParams) (Link, error) CreateOrganization(ctx context.Context, name string) (Organization, error) CreateUser(ctx context.Context, arg CreateUserParams) (User, error) DeleteEvent(ctx context.Context, eid uuid.UUID) error + DeleteLink(ctx context.Context, lid uuid.UUID) error DeleteOrganization(ctx context.Context, oid uuid.UUID) error DeleteUser(ctx context.Context, uid uuid.UUID) error // Bot Token Queries GetBotTokenByHash(ctx context.Context, tokenHash string) (BotToken, error) GetEventByID(ctx context.Context, eid uuid.UUID) (Event, error) GetEventRegistrations(ctx context.Context, eid uuid.UUID) ([]GetEventRegistrationsRow, error) + GetLinkByEndpointURL(ctx context.Context, endpointUrl string) (Link, error) + GetLinkByLID(ctx context.Context, lid uuid.UUID) (Link, error) GetOrgMembers(ctx context.Context, oid uuid.UUID) ([]GetOrgMembersRow, error) GetOrganizationByID(ctx context.Context, oid uuid.UUID) (Organization, error) + GetTotalVisits(ctx context.Context, lid uuid.UUID) (int64, error) GetUserByEmail(ctx context.Context, personalEmail pgtype.Text) (User, error) GetUserByID(ctx context.Context, uid uuid.UUID) (User, error) GetUserEvents(ctx context.Context, uid uuid.UUID) ([]GetUserEventsRow, error) @@ -36,14 +42,17 @@ type Querier interface { ListBotTokens(ctx context.Context) ([]ListBotTokensRow, error) ListEvents(ctx context.Context, arg ListEventsParams) ([]Event, error) ListEventsByOrg(ctx context.Context, arg ListEventsByOrgParams) ([]Event, error) + ListLinksByOrg(ctx context.Context, oid uuid.UUID) ([]Link, error) ListOrganizations(ctx context.Context, arg ListOrganizationsParams) ([]Organization, error) ListUsers(ctx context.Context, arg ListUsersParams) ([]User, error) + LogLinkVisit(ctx context.Context, arg LogLinkVisitParams) (LinkVisit, error) RegisterForEvent(ctx context.Context, arg RegisterForEventParams) error RemoveOrgMember(ctx context.Context, arg RemoveOrgMemberParams) error RevokeBotToken(ctx context.Context, tokenID uuid.UUID) error UnregisterFromEvent(ctx context.Context, arg UnregisterFromEventParams) error UpdateBotTokenLastUsed(ctx context.Context, tokenID uuid.UUID) error UpdateEvent(ctx context.Context, arg UpdateEventParams) (Event, error) + UpdateLink(ctx context.Context, arg UpdateLinkParams) (Link, error) UpdateOrganization(ctx context.Context, arg UpdateOrganizationParams) (Organization, error) UpdateUser(ctx context.Context, arg UpdateUserParams) (User, error) } diff --git a/internal/database/queries.sql b/internal/database/queries.sql index 426505f..8b29c7e 100644 --- a/internal/database/queries.sql +++ b/internal/database/queries.sql @@ -152,3 +152,37 @@ UPDATE bot_tokens SET is_active = false WHERE token_id = $1; -- name: UpdateBotTokenLastUsed :exec UPDATE bot_tokens SET last_used_at = CURRENT_TIMESTAMP WHERE token_id = $1; + +-- Link Queries + +-- name: CreateLink :one +INSERT INTO links (endpoint_url, dest_url, oid) +VALUES ($1, $2, $3) +RETURNING *; + +-- name: GetLinkByLID :one +SELECT * FROM links WHERE lid = $1; + +-- name: GetLinkByEndpointURL :one +SELECT * FROM links WHERE endpoint_url = $1; + +-- name: UpdateLink :one +UPDATE links +SET endpoint_url = COALESCE(sqlc.narg('endpoint_url'), endpoint_url), + dest_url = COALESCE(sqlc.narg('dest_url'), dest_url) +WHERE lid = $1 +RETURNING *; + +-- name: DeleteLink :exec +DELETE FROM links WHERE lid = $1; + +-- name: ListLinksByOrg :many +SELECT * FROM links WHERE oid = $1 ORDER BY created_at DESC; + +-- name: LogLinkVisit :one +INSERT INTO link_visits (lid, uid) +VALUES ($1, $2) +RETURNING *; + +-- name: GetTotalVisits :one +SELECT COUNT(*) FROM link_visits WHERE lid = $1; diff --git a/internal/database/queries.sql.go b/internal/database/queries.sql.go index d9f267d..9a3aadc 100644 --- a/internal/database/queries.sql.go +++ b/internal/database/queries.sql.go @@ -105,6 +105,33 @@ func (q *Queries) CreateEvent(ctx context.Context, arg CreateEventParams) (Event return i, err } +const createLink = `-- name: CreateLink :one + +INSERT INTO links (endpoint_url, dest_url, oid) +VALUES ($1, $2, $3) +RETURNING lid, endpoint_url, dest_url, oid, created_at +` + +type CreateLinkParams struct { + EndpointUrl string `json:"endpoint_url"` + DestUrl string `json:"dest_url"` + Oid uuid.UUID `json:"oid"` +} + +// Link Queries +func (q *Queries) CreateLink(ctx context.Context, arg CreateLinkParams) (Link, error) { + row := q.db.QueryRow(ctx, createLink, arg.EndpointUrl, arg.DestUrl, arg.Oid) + var i Link + err := row.Scan( + &i.Lid, + &i.EndpointUrl, + &i.DestUrl, + &i.Oid, + &i.CreatedAt, + ) + return i, err +} + const createOrganization = `-- name: CreateOrganization :one INSERT INTO organizations (name) VALUES ($1) @@ -174,6 +201,15 @@ func (q *Queries) DeleteEvent(ctx context.Context, eid uuid.UUID) error { return err } +const deleteLink = `-- name: DeleteLink :exec +DELETE FROM links WHERE lid = $1 +` + +func (q *Queries) DeleteLink(ctx context.Context, lid uuid.UUID) error { + _, err := q.db.Exec(ctx, deleteLink, lid) + return err +} + const deleteOrganization = `-- name: DeleteOrganization :exec DELETE FROM organizations WHERE oid = $1 ` @@ -290,6 +326,40 @@ func (q *Queries) GetEventRegistrations(ctx context.Context, eid uuid.UUID) ([]G return items, nil } +const getLinkByEndpointURL = `-- name: GetLinkByEndpointURL :one +SELECT lid, endpoint_url, dest_url, oid, created_at FROM links WHERE endpoint_url = $1 +` + +func (q *Queries) GetLinkByEndpointURL(ctx context.Context, endpointUrl string) (Link, error) { + row := q.db.QueryRow(ctx, getLinkByEndpointURL, endpointUrl) + var i Link + err := row.Scan( + &i.Lid, + &i.EndpointUrl, + &i.DestUrl, + &i.Oid, + &i.CreatedAt, + ) + return i, err +} + +const getLinkByLID = `-- name: GetLinkByLID :one +SELECT lid, endpoint_url, dest_url, oid, created_at FROM links WHERE lid = $1 +` + +func (q *Queries) GetLinkByLID(ctx context.Context, lid uuid.UUID) (Link, error) { + row := q.db.QueryRow(ctx, getLinkByLID, lid) + var i Link + err := row.Scan( + &i.Lid, + &i.EndpointUrl, + &i.DestUrl, + &i.Oid, + &i.CreatedAt, + ) + return i, err +} + const getOrgMembers = `-- name: GetOrgMembers :many SELECT u.uid, u.first_name, u.last_name, u.personal_email, u.school_email, u.phone, u.grad_year, u.role, u.date_created, u.date_modified, om.is_admin, om.date_joined, om.last_active FROM users u @@ -364,6 +434,17 @@ func (q *Queries) GetOrganizationByID(ctx context.Context, oid uuid.UUID) (Organ return i, err } +const getTotalVisits = `-- name: GetTotalVisits :one +SELECT COUNT(*) FROM link_visits WHERE lid = $1 +` + +func (q *Queries) GetTotalVisits(ctx context.Context, lid uuid.UUID) (int64, error) { + row := q.db.QueryRow(ctx, getTotalVisits, lid) + var count int64 + err := row.Scan(&count) + return count, err +} + const getUserByEmail = `-- name: GetUserByEmail :one SELECT uid, first_name, last_name, personal_email, school_email, phone, grad_year, role, date_created, date_modified FROM users WHERE personal_email = $1 OR school_email = $1 ` @@ -656,6 +737,36 @@ func (q *Queries) ListEventsByOrg(ctx context.Context, arg ListEventsByOrgParams return items, nil } +const listLinksByOrg = `-- name: ListLinksByOrg :many +SELECT lid, endpoint_url, dest_url, oid, created_at FROM links WHERE oid = $1 ORDER BY created_at DESC +` + +func (q *Queries) ListLinksByOrg(ctx context.Context, oid uuid.UUID) ([]Link, error) { + rows, err := q.db.Query(ctx, listLinksByOrg, oid) + if err != nil { + return nil, err + } + defer rows.Close() + items := []Link{} + for rows.Next() { + var i Link + if err := rows.Scan( + &i.Lid, + &i.EndpointUrl, + &i.DestUrl, + &i.Oid, + &i.CreatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const listOrganizations = `-- name: ListOrganizations :many SELECT oid, name, date_created, date_modified FROM organizations ORDER BY name LIMIT $1 OFFSET $2 ` @@ -730,6 +841,29 @@ func (q *Queries) ListUsers(ctx context.Context, arg ListUsersParams) ([]User, e return items, nil } +const logLinkVisit = `-- name: LogLinkVisit :one +INSERT INTO link_visits (lid, uid) +VALUES ($1, $2) +RETURNING lvid, lid, uid, created_at +` + +type LogLinkVisitParams struct { + Lid uuid.UUID `json:"lid"` + Uid pgtype.UUID `json:"uid"` +} + +func (q *Queries) LogLinkVisit(ctx context.Context, arg LogLinkVisitParams) (LinkVisit, error) { + row := q.db.QueryRow(ctx, logLinkVisit, arg.Lid, arg.Uid) + var i LinkVisit + err := row.Scan( + &i.Lvid, + &i.Lid, + &i.Uid, + &i.CreatedAt, + ) + return i, err +} + const registerForEvent = `-- name: RegisterForEvent :exec INSERT INTO event_registrations (uid, eid, is_attending) VALUES ($1, $2, $3) @@ -828,6 +962,33 @@ func (q *Queries) UpdateEvent(ctx context.Context, arg UpdateEventParams) (Event return i, err } +const updateLink = `-- name: UpdateLink :one +UPDATE links +SET endpoint_url = COALESCE($2, endpoint_url), + dest_url = COALESCE($3, dest_url) +WHERE lid = $1 +RETURNING lid, endpoint_url, dest_url, oid, created_at +` + +type UpdateLinkParams struct { + Lid uuid.UUID `json:"lid"` + EndpointUrl pgtype.Text `json:"endpoint_url"` + DestUrl pgtype.Text `json:"dest_url"` +} + +func (q *Queries) UpdateLink(ctx context.Context, arg UpdateLinkParams) (Link, error) { + row := q.db.QueryRow(ctx, updateLink, arg.Lid, arg.EndpointUrl, arg.DestUrl) + var i Link + err := row.Scan( + &i.Lid, + &i.EndpointUrl, + &i.DestUrl, + &i.Oid, + &i.CreatedAt, + ) + return i, err +} + const updateOrganization = `-- name: UpdateOrganization :one UPDATE organizations SET name = COALESCE($2, name) diff --git a/internal/dto/dto.go b/internal/dto/dto.go index cabfa00..398db56 100644 --- a/internal/dto/dto.go +++ b/internal/dto/dto.go @@ -118,6 +118,34 @@ type RegisterEventRequest struct { IsAttending bool `json:"is_attending"` } +// ============================================================================ +// Link DTOs +// ============================================================================ + +type CreateLinkRequest struct { + EndpointURL string `json:"endpoint_url" validate:"required,alphanumhyphen"` + DestURL string `json:"dest_url" validate:"required,url"` + OrgID uuid.UUID `json:"org_id" validate:"required"` +} + +type UpdateLinkRequest struct { + EndpointURL *string `json:"endpoint_url,omitempty" validate:"omitempty,alphanumhyphen"` + DestURL *string `json:"dest_url,omitempty" validate:"omitempty,url"` +} + +type LinkResponse struct { + LID uuid.UUID `json:"lid"` + EndpointURL string `json:"endpoint_url"` + DestURL string `json:"dest_url"` + OrgID uuid.UUID `json:"org_id"` + CreatedAt *time.Time `json:"created_at,omitempty"` +} + +type VisitCountResponse struct { + LID uuid.UUID `json:"lid"` + Count int64 `json:"count"` +} + // ============================================================================ // Pagination // ============================================================================ diff --git a/internal/handler/links.go b/internal/handler/links.go new file mode 100644 index 0000000..c0713f5 --- /dev/null +++ b/internal/handler/links.go @@ -0,0 +1,311 @@ +package handler + +import ( + "context" + "encoding/json" + "io" + "log/slog" + "net/http" + + "github.com/capyrpi/api/internal/database" + "github.com/capyrpi/api/internal/dto" + "github.com/capyrpi/api/internal/middleware" + "github.com/go-chi/chi/v5" + "github.com/google/uuid" + "github.com/jackc/pgx/v5/pgtype" + "github.com/yeqown/go-qrcode/v2" + "github.com/yeqown/go-qrcode/writer/standard" +) + +// CreateLink creates a new dynamic link +// @Summary Create link +// @Description Creates a new dynamic link for an organization. Requires org_admin role. +// @Tags links +// @Accept json +// @Produce json +// @Param body body dto.CreateLinkRequest true "Link data" +// @Success 201 {object} dto.LinkResponse +// @Failure 400 {object} ErrorResponse +// @Failure 403 {object} ErrorResponse +// @Security CookieAuth +// @Router /links [post] +func (h *Handler) CreateLink(w http.ResponseWriter, r *http.Request) { + var req dto.CreateLinkRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + h.respondError(w, http.StatusBadRequest, "Invalid request body") + return + } + + if req.OrgID == uuid.Nil { + h.respondError(w, http.StatusBadRequest, "org_id is required") + return + } + + // Check if user is admin of the org + claims, ok := middleware.GetUserClaims(r.Context()) + if !ok { + h.respondError(w, http.StatusUnauthorized, "Unauthorized") + return + } + uid, _ := uuid.Parse(claims.UserID) + + isAdmin, err := h.queries.IsOrgAdmin(r.Context(), database.IsOrgAdminParams{ + Uid: uid, + Oid: req.OrgID, + }) + if err != nil { + h.handleDBError(w, err) + return + } + if !isAdmin.Bool { + h.respondError(w, http.StatusForbidden, "Only org admins can create links") + return + } + + link, err := h.queries.CreateLink(r.Context(), database.CreateLinkParams{ + EndpointUrl: req.EndpointURL, + DestUrl: req.DestURL, + Oid: req.OrgID, + }) + if err != nil { + h.handleDBError(w, err) + return + } + + h.respondJSON(w, http.StatusCreated, toLinkResponse(link)) +} + +// UpdateLink updates an existing dynamic link +// @Summary Update link +// @Description Updates a dynamic link's destination or endpoint URL. Requires org_admin role. +// @Tags links +// @Accept json +// @Produce json +// @Param lid path string true "Link UUID" +// @Param body body dto.UpdateLinkRequest true "Update data" +// @Success 200 {object} dto.LinkResponse +// @Failure 400 {object} ErrorResponse +// @Failure 403 {object} ErrorResponse +// @Failure 404 {object} ErrorResponse +// @Security CookieAuth +// @Router /links/{lid} [put] +func (h *Handler) UpdateLink(w http.ResponseWriter, r *http.Request) { + lidStr := chi.URLParam(r, "lid") + lid, err := uuid.Parse(lidStr) + if err != nil { + h.respondError(w, http.StatusBadRequest, "Invalid link ID") + return + } + + var req dto.UpdateLinkRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + h.respondError(w, http.StatusBadRequest, "Invalid request body") + return + } + + link, err := h.queries.GetLinkByLID(r.Context(), lid) + if err != nil { + h.handleDBError(w, err) + return + } + + // Permission check + claims, ok := middleware.GetUserClaims(r.Context()) + if !ok { + h.respondError(w, http.StatusUnauthorized, "Unauthorized") + return + } + uid, _ := uuid.Parse(claims.UserID) + + isAdmin, err := h.queries.IsOrgAdmin(r.Context(), database.IsOrgAdminParams{ + Uid: uid, + Oid: link.Oid, + }) + if err != nil { + h.handleDBError(w, err) + return + } + if !isAdmin.Bool { + h.respondError(w, http.StatusForbidden, "Only org admins can update links") + return + } + + updatedLink, err := h.queries.UpdateLink(r.Context(), database.UpdateLinkParams{ + Lid: lid, + EndpointUrl: toPgText(req.EndpointURL), + DestUrl: toPgText(req.DestURL), + }) + if err != nil { + h.handleDBError(w, err) + return + } + + h.respondJSON(w, http.StatusOK, toLinkResponse(updatedLink)) +} + +// ResolveLink resolves a dynamic link, logs a visit, and redirects +// @Summary Resolve link +// @Description Redirects to the destination URL and logs a visit +// @Tags links +// @Param endpoint_url path string true "Dynamic link endpoint URL" +// @Success 302 +// @Failure 404 {object} ErrorResponse +// @Router /r/{endpoint_url} [get] +func (h *Handler) ResolveLink(w http.ResponseWriter, r *http.Request) { + endpointURL := chi.URLParam(r, "endpoint_url") + + link, err := h.queries.GetLinkByEndpointURL(r.Context(), endpointURL) + if err != nil { + h.handleDBError(w, err) + return + } + + // Log visit asynchronously to not block the redirect + go func() { + // Create a detached context so the DB query isn't cancelled when the HTTP request ends + ctx := context.Background() + + var uid pgtype.UUID + claims, ok := middleware.GetUserClaims(r.Context()) + if ok { + parsedUID, err := uuid.Parse(claims.UserID) + if err == nil { + uid = pgtype.UUID{Bytes: parsedUID, Valid: true} + } + } + + _, err := h.queries.LogLinkVisit(ctx, database.LogLinkVisitParams{ + Lid: link.Lid, + Uid: uid, + }) + if err != nil { + slog.Error("failed to log link visit", "lid", link.Lid, "error", err) + } + }() + + http.Redirect(w, r, link.DestUrl, http.StatusFound) +} + +// ListOrgLinks lists all links for an organization +// @Summary List org links +// @Description Returns all dynamic links owned by an organization +// @Tags links +// @Accept json +// @Produce json +// @Param oid path string true "Organization UUID" +// @Success 200 {array} dto.LinkResponse +// @Failure 404 {object} ErrorResponse +// @Security CookieAuth +// @Router /organizations/{oid}/links [get] +func (h *Handler) ListOrgLinks(w http.ResponseWriter, r *http.Request) { + oidStr := chi.URLParam(r, "oid") + oid, err := uuid.Parse(oidStr) + if err != nil { + h.respondError(w, http.StatusBadRequest, "Invalid organization ID") + return + } + + links, err := h.queries.ListLinksByOrg(r.Context(), oid) + if err != nil { + h.handleDBError(w, err) + return + } + + response := make([]dto.LinkResponse, len(links)) + for i, l := range links { + response[i] = toLinkResponse(l) + } + + h.respondJSON(w, http.StatusOK, response) +} + +// GetTotalVisits returns the total number of visits for a link +// @Summary Get visit count +// @Description Returns the total number of visits logged for a link +// @Tags links +// @Produce json +// @Param lid path string true "Link UUID" +// @Success 200 {object} dto.VisitCountResponse +// @Failure 404 {object} ErrorResponse +// @Security CookieAuth +// @Router /links/{lid}/visits [get] +func (h *Handler) GetTotalVisits(w http.ResponseWriter, r *http.Request) { + lidStr := chi.URLParam(r, "lid") + lid, err := uuid.Parse(lidStr) + if err != nil { + h.respondError(w, http.StatusBadRequest, "Invalid link ID") + return + } + + count, err := h.queries.GetTotalVisits(r.Context(), lid) + if err != nil { + h.handleDBError(w, err) + return + } + + h.respondJSON(w, http.StatusOK, dto.VisitCountResponse{ + LID: lid, + Count: count, + }) +} + +type nopWriteCloser struct { + io.Writer +} + +func (nopWriteCloser) Close() error { return nil } + +// GetQRCode generates a QR code for a link's destination URL +// @Summary Get QR code +// @Description Generates and returns a QR code image for the link's destination URL +// @Tags links +// @Produce image/png +// @Param lid path string true "Link UUID" +// @Success 200 {file} image/png +// @Failure 404 {object} ErrorResponse +// @Router /links/{lid}/qrcode [get] +func (h *Handler) GetQRCode(w http.ResponseWriter, r *http.Request) { + lidStr := chi.URLParam(r, "lid") + lid, err := uuid.Parse(lidStr) + if err != nil { + h.respondError(w, http.StatusBadRequest, "Invalid link ID") + return + } + + link, err := h.queries.GetLinkByLID(r.Context(), lid) + if err != nil { + h.handleDBError(w, err) + return + } + + qrc, err := qrcode.New(link.DestUrl) + if err != nil { + slog.Error("failed to generate QR code", "error", err) + h.respondError(w, http.StatusInternalServerError, "Failed to generate QR code") + return + } + + w.Header().Set("Content-Type", "image/png") + wr := standard.NewWithWriter(nopWriteCloser{w}) + if err != nil { + slog.Error("failed to create standard writer for QR code", "error", err) + h.respondError(w, http.StatusInternalServerError, "Failed to create QR code writer") + return + } + + if err = qrc.Save(wr); err != nil { + slog.Error("failed to write QR code to response", "error", err) + // Header already set, but we can't do much now if it partially wrote + } +} + +// Helper functions for link conversion +func toLinkResponse(link database.Link) dto.LinkResponse { + return dto.LinkResponse{ + LID: link.Lid, + EndpointURL: link.EndpointUrl, + DestURL: link.DestUrl, + OrgID: link.Oid, + CreatedAt: fromPgTimestamp(link.CreatedAt), + } +} diff --git a/internal/handler/links_test.go b/internal/handler/links_test.go new file mode 100644 index 0000000..7744488 --- /dev/null +++ b/internal/handler/links_test.go @@ -0,0 +1,180 @@ +package handler_test + +import ( + "bytes" + "context" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "github.com/capyrpi/api/internal/config" + "github.com/capyrpi/api/internal/database" + "github.com/capyrpi/api/internal/database/mocks" + "github.com/capyrpi/api/internal/dto" + "github.com/capyrpi/api/internal/handler" + "github.com/capyrpi/api/internal/middleware" + "github.com/go-chi/chi/v5" + "github.com/google/uuid" + "github.com/jackc/pgx/v5/pgtype" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func TestCreateLink(t *testing.T) { + uid := uuid.New() + oid := uuid.New() + lid := uuid.New() + + tests := []struct { + name string + requestBody interface{} + setupMock func(*mocks.Querier) + setupContext func() context.Context + expectedStatus int + }{ + { + name: "Success", + requestBody: dto.CreateLinkRequest{ + EndpointURL: "promo-2024", + DestURL: "https://capyrpi.org/promo", + OrgID: oid, + }, + setupMock: func(m *mocks.Querier) { + m.On("IsOrgAdmin", mock.Anything, database.IsOrgAdminParams{ + Uid: uid, + Oid: oid, + }).Return(pgtype.Bool{Bool: true, Valid: true}, nil) + + m.On("CreateLink", mock.Anything, database.CreateLinkParams{ + EndpointUrl: "promo-2024", + DestUrl: "https://capyrpi.org/promo", + Oid: oid, + }).Return(database.Link{ + Lid: lid, + EndpointUrl: "promo-2024", + DestUrl: "https://capyrpi.org/promo", + Oid: oid, + }, nil) + }, + setupContext: func() context.Context { + ctx := context.Background() + claims := &middleware.UserClaims{UserID: uid.String()} + return context.WithValue(ctx, middleware.UserClaimsKey, claims) + }, + expectedStatus: http.StatusCreated, + }, + { + name: "Forbidden - Not Admin", + requestBody: dto.CreateLinkRequest{ + EndpointURL: "promo-2024", + DestURL: "https://capyrpi.org/promo", + OrgID: oid, + }, + setupMock: func(m *mocks.Querier) { + m.On("IsOrgAdmin", mock.Anything, database.IsOrgAdminParams{ + Uid: uid, + Oid: oid, + }).Return(pgtype.Bool{Bool: false, Valid: true}, nil) + }, + setupContext: func() context.Context { + ctx := context.Background() + claims := &middleware.UserClaims{UserID: uid.String()} + return context.WithValue(ctx, middleware.UserClaimsKey, claims) + }, + expectedStatus: http.StatusForbidden, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockQueries := mocks.NewQuerier(t) + if tt.setupMock != nil { + tt.setupMock(mockQueries) + } + + h := handler.New(mockQueries, &config.Config{}) + + body, _ := json.Marshal(tt.requestBody) + req := httptest.NewRequest("POST", "/links", bytes.NewBuffer(body)) + req = req.WithContext(tt.setupContext()) + rr := httptest.NewRecorder() + + http.HandlerFunc(h.CreateLink).ServeHTTP(rr, req) + + assert.Equal(t, tt.expectedStatus, rr.Code) + if tt.expectedStatus == http.StatusCreated { + var res dto.LinkResponse + err := json.Unmarshal(rr.Body.Bytes(), &res) + assert.NoError(t, err) + assert.Equal(t, lid, res.LID) + assert.Equal(t, "promo-2024", res.EndpointURL) + } + }) + } +} + +func TestResolveLink(t *testing.T) { + lid := uuid.New() + endpoint := "my-link" + dest := "https://example.com" + + tests := []struct { + name string + endpointParam string + setupMock func(*mocks.Querier) + setupContext func() context.Context + expectedStatus int + expectedLoc string + }{ + { + name: "Success Redirect", + endpointParam: endpoint, + setupMock: func(m *mocks.Querier) { + m.On("GetLinkByEndpointURL", mock.Anything, endpoint).Return(database.Link{ + Lid: lid, + EndpointUrl: endpoint, + DestUrl: dest, + }, nil) + + // We don't strictly mock the background Context visit log here + // since it happens in a goroutine and is hard to sync without sleep or waitgroups, + // but let's mock it to avoid panic if the mock is strict. + m.On("LogLinkVisit", mock.Anything, mock.MatchedBy(func(p database.LogLinkVisitParams) bool { + return p.Lid == lid + })).Return(database.LinkVisit{}, nil).Maybe() + }, + setupContext: func() context.Context { + return context.Background() + }, + expectedStatus: http.StatusFound, // 302 + expectedLoc: dest, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockQueries := mocks.NewQuerier(t) + tt.setupMock(mockQueries) + + h := handler.New(mockQueries, &config.Config{}) + + req := httptest.NewRequest("GET", "/r/"+tt.endpointParam, nil) + req = req.WithContext(tt.setupContext()) + + // Setup chi router context so chi.URLParam works + rctx := chi.NewRouteContext() + rctx.URLParams.Add("endpoint_url", tt.endpointParam) + req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) + + rr := httptest.NewRecorder() + + http.HandlerFunc(h.ResolveLink).ServeHTTP(rr, req) + + assert.Equal(t, tt.expectedStatus, rr.Code) + if tt.expectedStatus == http.StatusFound { + assert.Equal(t, tt.expectedLoc, rr.Header().Get("Location")) + } + }) + } +} diff --git a/internal/router/router.go b/internal/router/router.go index 97c2cb1..cfa0dce 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -25,6 +25,9 @@ func New(h *handler.Handler, queries database.Querier, jwtSecret string, allowed // Health check (public) r.Get("/health", h.Health) + // Link resolution (public) + r.Get("/r/{endpoint_url}", h.ResolveLink) + // Swagger UI (public) - Only in non-production environments if h.Config.Env != "production" { r.Get("/swagger/*", httpSwagger.WrapHandler) @@ -72,6 +75,7 @@ func New(h *handler.Handler, queries database.Querier, jwtSecret string, allowed r.Post("/{oid}/members", h.AddOrgMember) r.Delete("/{oid}/members/{uid}", h.RemoveOrgMember) r.Get("/{oid}/events", h.ListOrgEvents) + r.Get("/{oid}/links", h.ListOrgLinks) }) // Events @@ -87,6 +91,14 @@ func New(h *handler.Handler, queries database.Querier, jwtSecret string, allowed r.Delete("/{eid}/register", h.UnregisterFromEvent) }) + // Links + r.Route("/links", func(r chi.Router) { + r.Post("/", h.CreateLink) + r.Put("/{lid}", h.UpdateLink) + r.Get("/{lid}/visits", h.GetTotalVisits) + r.Get("/{lid}/qrcode", h.GetQRCode) + }) + // Bot token management (human auth only) r.Route("/bot/tokens", func(r chi.Router) { r.Get("/", h.ListBotTokens) diff --git a/schema.sql b/schema.sql index 160c511..d8a6b3f 100644 --- a/schema.sql +++ b/schema.sql @@ -14,7 +14,7 @@ $$ language 'plpgsql'; -- 2. Tables CREATE TABLE IF NOT EXISTS users ( - uid UUID PRIMARY KEY DEFAULT gen_random_uuid(), + uid UUID PRIMARY KEY DEFAULT uuidv4(), first_name TEXT NOT NULL, last_name TEXT NOT NULL, personal_email TEXT UNIQUE, @@ -27,7 +27,7 @@ CREATE TABLE IF NOT EXISTS users ( ); CREATE TABLE IF NOT EXISTS organizations ( - oid UUID PRIMARY KEY DEFAULT gen_random_uuid(), + oid UUID PRIMARY KEY DEFAULT uuidv4(), name TEXT NOT NULL, date_created DATE DEFAULT CURRENT_DATE, date_modified DATE DEFAULT CURRENT_DATE @@ -43,7 +43,7 @@ CREATE TABLE IF NOT EXISTS org_members ( ); CREATE TABLE IF NOT EXISTS events ( - eid UUID PRIMARY KEY DEFAULT gen_random_uuid(), + eid UUID PRIMARY KEY DEFAULT uuidv4(), location TEXT, event_time TIMESTAMP, description TEXT, @@ -68,7 +68,7 @@ CREATE TABLE IF NOT EXISTS event_registrations ( -- 3. Bot Tokens (global access for M2M authentication) CREATE TABLE IF NOT EXISTS bot_tokens ( - token_id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + token_id UUID PRIMARY KEY DEFAULT uuidv4(), token_hash TEXT NOT NULL, -- bcrypt hash of the token name TEXT NOT NULL, -- human-readable name for the bot created_by UUID NOT NULL REFERENCES users(uid), @@ -78,6 +78,21 @@ CREATE TABLE IF NOT EXISTS bot_tokens ( is_active BOOLEAN DEFAULT TRUE ); +CREATE TABLE IF NOT EXISTS links ( + lid UUID PRIMARY KEY DEFAULT uuidv4(), + endpoint_url TEXT NOT NULL UNIQUE, + dest_url TEXT NOT NULL, + oid UUID NOT NULL REFERENCES organizations(oid) ON DELETE CASCADE, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE IF NOT EXISTS link_visits ( + lvid UUID PRIMARY KEY DEFAULT uuidv7(), + lid UUID NOT NULL REFERENCES links(lid) ON DELETE CASCADE, + uid UUID REFERENCES users(uid) ON DELETE SET NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + CREATE INDEX IF NOT EXISTS idx_bot_tokens_active ON bot_tokens(is_active) WHERE is_active = TRUE; -- 4. Triggers diff --git a/tests/benchmarks/suite_test.go b/tests/benchmarks/suite_test.go index 6d950d3..5c63e37 100644 --- a/tests/benchmarks/suite_test.go +++ b/tests/benchmarks/suite_test.go @@ -48,7 +48,7 @@ func TestMain(m *testing.M) { log.Printf("Using schema from: %s", schemaPath) pgContainer, err := postgres.Run(ctx, - "postgres:16-alpine", + "postgres:18-alpine", postgres.WithInitScripts(schemaPath), postgres.WithDatabase("bench_db"), postgres.WithUsername("bench"), From 9fc6d69955976703ef632fe1db5b53db367944d5 Mon Sep 17 00:00:00 2001 From: Jeremy <87028711+jgoldberger26@users.noreply.github.com> Date: Mon, 23 Feb 2026 12:30:21 -0500 Subject: [PATCH 3/8] Make QR Code return endpoint url If the QR code returns the dest url, visits to that URL cannot be tracked. Use endpoint url instead. --- internal/handler/links.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/handler/links.go b/internal/handler/links.go index c0713f5..e1573ca 100644 --- a/internal/handler/links.go +++ b/internal/handler/links.go @@ -278,7 +278,7 @@ func (h *Handler) GetQRCode(w http.ResponseWriter, r *http.Request) { return } - qrc, err := qrcode.New(link.DestUrl) + qrc, err := qrcode.New(link.EndpointUrl) if err != nil { slog.Error("failed to generate QR code", "error", err) h.respondError(w, http.StatusInternalServerError, "Failed to generate QR code") From 31ab83036d6be98bda39edd208ec0bc7d4a8f989 Mon Sep 17 00:00:00 2001 From: Jeremy <87028711+jgoldberger26@users.noreply.github.com> Date: Mon, 23 Feb 2026 12:30:38 -0500 Subject: [PATCH 4/8] Add link integration tests --- tests/integration/links_test.go | 177 ++++++++++++++++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 tests/integration/links_test.go diff --git a/tests/integration/links_test.go b/tests/integration/links_test.go new file mode 100644 index 0000000..bead82a --- /dev/null +++ b/tests/integration/links_test.go @@ -0,0 +1,177 @@ +//go:build integration + +package integration + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/capyrpi/api/internal/config" + "github.com/capyrpi/api/internal/database" + "github.com/capyrpi/api/internal/dto" + "github.com/capyrpi/api/internal/handler" + "github.com/capyrpi/api/internal/middleware" + "github.com/capyrpi/api/internal/router" + "github.com/capyrpi/api/internal/testutils" + "github.com/golang-jwt/jwt/v5" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestLinkFlow(t *testing.T) { + // 1. Setup DB and Server + pool := testutils.SetupTestDB(t) + defer pool.Close() + + queries := database.New(pool) + cfg := &config.Config{JWT: config.JWTConfig{Secret: "test-secret", ExpiryHours: 1}} + h := handler.New(queries, cfg) + r := router.New(h, queries, cfg.JWT.Secret, []string{}) + server := httptest.NewServer(r) + defer server.Close() + client := server.Client() + + // Disable redirects on the client so we can test the 302 response directly + client.CheckRedirect = func(req *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse + } + + ctx := context.Background() + + // 2. Create User + user, err := queries.CreateUser(ctx, database.CreateUserParams{ + FirstName: "Link", + LastName: "Tester", + Role: database.NullUserRole{UserRole: database.UserRoleStudent, Valid: true}, + }) + require.NoError(t, err) + + // 3. Generate Auth Token + claims := middleware.UserClaims{ + UserID: user.Uid.String(), + RegisteredClaims: jwt.RegisteredClaims{ + ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)), + }, + } + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + tokenString, err := token.SignedString([]byte(cfg.JWT.Secret)) + require.NoError(t, err) + cookie := &http.Cookie{Name: "capy_auth", Value: tokenString} + + // 4. Create Organization + orgBody := []byte(`{"name":"Link Org","slug":"link-org"}`) + req, _ := http.NewRequest("POST", server.URL+"/v1/organizations", bytes.NewBuffer(orgBody)) + req.AddCookie(cookie) + req.Header.Set("Content-Type", "application/json") + resp, err := client.Do(req) + require.NoError(t, err) + defer resp.Body.Close() + require.Equal(t, http.StatusCreated, resp.StatusCode) + + var orgResp dto.OrganizationResponse + err = json.NewDecoder(resp.Body).Decode(&orgResp) + require.NoError(t, err) + oid := orgResp.OID.String() + + // 5. Create Link + linkReq := dto.CreateLinkRequest{ + EndpointURL: "my-promo", + DestURL: "https://example.com/dest", + OrgID: orgResp.OID, + } + linkBody, _ := json.Marshal(linkReq) + req, _ = http.NewRequest("POST", server.URL+"/v1/links", bytes.NewBuffer(linkBody)) + req.AddCookie(cookie) + req.Header.Set("Content-Type", "application/json") + + resp, err = client.Do(req) + require.NoError(t, err) + defer resp.Body.Close() + require.Equal(t, http.StatusCreated, resp.StatusCode) + + var linkResp dto.LinkResponse + err = json.NewDecoder(resp.Body).Decode(&linkResp) + require.NoError(t, err) + require.Equal(t, "my-promo", linkResp.EndpointURL) + lid := linkResp.LID.String() + + // 6. Resolve Link (Simulate user clicking it) + req, _ = http.NewRequest("GET", server.URL+"/r/my-promo", nil) + resp, err = client.Do(req) + require.NoError(t, err) + defer resp.Body.Close() + + // Check redirect status and location + assert.Equal(t, http.StatusFound, resp.StatusCode) + assert.Equal(t, "https://example.com/dest", resp.Header.Get("Location")) + + // Give the async goroutine a tiny bit of time to log the visit in DB + time.Sleep(100 * time.Millisecond) + + // 7. Check Visit Count + req, _ = http.NewRequest("GET", fmt.Sprintf("%s/v1/links/%s/visits", server.URL, lid), nil) + req.AddCookie(cookie) + resp, err = client.Do(req) + require.NoError(t, err) + defer resp.Body.Close() + require.Equal(t, http.StatusOK, resp.StatusCode) + + var visitResp dto.VisitCountResponse + err = json.NewDecoder(resp.Body).Decode(&visitResp) + require.NoError(t, err) + assert.Equal(t, int64(1), visitResp.Count) + + // 8. Update Link + updateReq := dto.UpdateLinkRequest{ + EndpointURL: nil, + DestURL: toPtr("https://example.com/updated"), + } + updateBody, _ := json.Marshal(updateReq) + req, _ = http.NewRequest("PUT", fmt.Sprintf("%s/v1/links/%s", server.URL, lid), bytes.NewBuffer(updateBody)) + req.AddCookie(cookie) + req.Header.Set("Content-Type", "application/json") + + resp, err = client.Do(req) + require.NoError(t, err) + defer resp.Body.Close() + require.Equal(t, http.StatusOK, resp.StatusCode) + + var updatedLink dto.LinkResponse + err = json.NewDecoder(resp.Body).Decode(&updatedLink) + require.NoError(t, err) + assert.Equal(t, "https://example.com/updated", updatedLink.DestURL) + + // 9. List Org Links + req, _ = http.NewRequest("GET", fmt.Sprintf("%s/v1/organizations/%s/links", server.URL, oid), nil) + req.AddCookie(cookie) + resp, err = client.Do(req) + require.NoError(t, err) + defer resp.Body.Close() + require.Equal(t, http.StatusOK, resp.StatusCode) + + var links []dto.LinkResponse + err = json.NewDecoder(resp.Body).Decode(&links) + require.NoError(t, err) + require.Len(t, links, 1) + + // 10. Get QR Code + req, _ = http.NewRequest("GET", fmt.Sprintf("%s/v1/links/%s/qrcode", server.URL, lid), nil) + // We don't usually need auth for QR code, but the endpoint currently requires CookieAuth based on swagger tags + // Actually looking at router.go, /links/{lid}/qrcode is under protected group. + req.AddCookie(cookie) + resp, err = client.Do(req) + require.NoError(t, err) + defer resp.Body.Close() + require.Equal(t, http.StatusOK, resp.StatusCode) + assert.Equal(t, "image/png", resp.Header.Get("Content-Type")) +} + +func toPtr(s string) *string { + return &s +} From 746f0e629de6d88cda58eea8dc2369783e6f0423 Mon Sep 17 00:00:00 2001 From: Jeremy <87028711+jgoldberger26@users.noreply.github.com> Date: Thu, 19 Mar 2026 19:05:57 -0400 Subject: [PATCH 5/8] Add dots to QR code It looks terrible this is going away --- internal/handler/links.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/internal/handler/links.go b/internal/handler/links.go index e1573ca..b19ff95 100644 --- a/internal/handler/links.go +++ b/internal/handler/links.go @@ -17,6 +17,9 @@ import ( "github.com/yeqown/go-qrcode/writer/standard" ) +const QR_FG_COLOR = "#067b76" +const QR_BG_COLOR = "#fcfdfe" + // CreateLink creates a new dynamic link // @Summary Create link // @Description Creates a new dynamic link for an organization. Requires org_admin role. @@ -278,15 +281,21 @@ func (h *Handler) GetQRCode(w http.ResponseWriter, r *http.Request) { return } - qrc, err := qrcode.New(link.EndpointUrl) + qrc, err := qrcode.NewWith(link.EndpointUrl) if err != nil { slog.Error("failed to generate QR code", "error", err) h.respondError(w, http.StatusInternalServerError, "Failed to generate QR code") return } + qrcOpts := []standard.ImageOption{ + standard.WithBgColorRGBHex(QR_BG_COLOR), + standard.WithFgColorRGBHex(QR_FG_COLOR), + standard.WithCircleShape(), + } + w.Header().Set("Content-Type", "image/png") - wr := standard.NewWithWriter(nopWriteCloser{w}) + wr := standard.NewWithWriter(nopWriteCloser{w}, qrcOpts...) if err != nil { slog.Error("failed to create standard writer for QR code", "error", err) h.respondError(w, http.StatusInternalServerError, "Failed to create QR code writer") From 2038bd5a7a1cdacc0b91865abf5e3a388b27b5eb Mon Sep 17 00:00:00 2001 From: Jeremy <87028711+jgoldberger26@users.noreply.github.com> Date: Thu, 26 Mar 2026 22:14:15 -0400 Subject: [PATCH 6/8] Use hardcoded public link endpoint --- internal/handler/links.go | 7 +++++-- internal/router/router.go | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/internal/handler/links.go b/internal/handler/links.go index b19ff95..57c13b0 100644 --- a/internal/handler/links.go +++ b/internal/handler/links.go @@ -20,6 +20,9 @@ import ( const QR_FG_COLOR = "#067b76" const QR_BG_COLOR = "#fcfdfe" +// TODO this should be a global variable, this is very brittle +const PUBLIC_LINK_ENDPOINT = "www.capyrpi.org/api/r/" + // CreateLink creates a new dynamic link // @Summary Create link // @Description Creates a new dynamic link for an organization. Requires org_admin role. @@ -281,7 +284,7 @@ func (h *Handler) GetQRCode(w http.ResponseWriter, r *http.Request) { return } - qrc, err := qrcode.NewWith(link.EndpointUrl) + qrc, err := qrcode.NewWith(PUBLIC_LINK_ENDPOINT + link.EndpointUrl) if err != nil { slog.Error("failed to generate QR code", "error", err) h.respondError(w, http.StatusInternalServerError, "Failed to generate QR code") @@ -291,7 +294,7 @@ func (h *Handler) GetQRCode(w http.ResponseWriter, r *http.Request) { qrcOpts := []standard.ImageOption{ standard.WithBgColorRGBHex(QR_BG_COLOR), standard.WithFgColorRGBHex(QR_FG_COLOR), - standard.WithCircleShape(), + // standard.WithCircleShape(), } w.Header().Set("Content-Type", "image/png") diff --git a/internal/router/router.go b/internal/router/router.go index cfa0dce..6d43cf9 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -26,6 +26,7 @@ func New(h *handler.Handler, queries database.Querier, jwtSecret string, allowed r.Get("/health", h.Health) // Link resolution (public) + // TODO Use a global variable to link this with links.go r.Get("/r/{endpoint_url}", h.ResolveLink) // Swagger UI (public) - Only in non-production environments From 7ddf171f560018206b81996f669861ea34e1f906 Mon Sep 17 00:00:00 2001 From: Jeremy <87028711+jgoldberger26@users.noreply.github.com> Date: Thu, 26 Mar 2026 23:08:24 -0400 Subject: [PATCH 7/8] Revert to postgres 16 and add migrate scripts --- .github/workflows/ci.yml | 2 +- README.md | 2 +- docker-compose.yml | 2 +- internal/router/router.go | 2 ++ internal/testutils/container.go | 2 +- migrations/20260326224918_add_links.down.sql | 2 ++ migrations/20260326224918_add_links.up.sql | 14 ++++++++++++++ tests/benchmarks/suite_test.go | 2 +- 8 files changed, 23 insertions(+), 5 deletions(-) create mode 100644 migrations/20260326224918_add_links.down.sql create mode 100644 migrations/20260326224918_add_links.up.sql diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 781d3ca..b252456 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ jobs: services: # Optional: We use testcontainers, but having a service is good for fallback or specific DB tests postgres: - image: postgres:18-alpine + image: postgres:16-alpine env: POSTGRES_USER: test POSTGRES_PASSWORD: test diff --git a/README.md b/README.md index ac0b90a..4ba4678 100644 --- a/README.md +++ b/README.md @@ -252,7 +252,7 @@ To run the full stack (API + Postgres + Cloudflare Tunnel), update your `.env` f ```yaml services: db: - image: postgres:18-alpine + image: postgres:16-alpine env_file: - .env environment: diff --git a/docker-compose.yml b/docker-compose.yml index 428fcf6..7c643db 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,6 @@ services: db: - image: postgres:18-alpine + image: postgres:16-alpine env_file: - .env environment: diff --git a/internal/router/router.go b/internal/router/router.go index 832527a..f70a12b 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -107,6 +107,8 @@ func New(h *handler.Handler, queries database.Querier, jwtSecret string, allowed }) }) + mountProtectedRoutes(r, h, jwtSecret) + // Bot routes (M2M auth) r.Group(func(r chi.Router) { r.Use(middleware.M2MAuth(queries)) diff --git a/internal/testutils/container.go b/internal/testutils/container.go index 920da82..8f507df 100644 --- a/internal/testutils/container.go +++ b/internal/testutils/container.go @@ -54,7 +54,7 @@ func SetupTestDB(t *testing.T) *pgxpool.Pool { schemaPath := filepath.Join(projectRoot, "schema.sql") pgContainer, err := postgres.Run(ctx, - "postgres:18-alpine", + "postgres:16-alpine", postgres.WithInitScripts(schemaPath), postgres.WithDatabase("test_db"), postgres.WithUsername("test"), diff --git a/migrations/20260326224918_add_links.down.sql b/migrations/20260326224918_add_links.down.sql new file mode 100644 index 0000000..108a49b --- /dev/null +++ b/migrations/20260326224918_add_links.down.sql @@ -0,0 +1,2 @@ +DROP TABLE IF EXISTS link_visits; +DROP TABLE IF EXISTS links; diff --git a/migrations/20260326224918_add_links.up.sql b/migrations/20260326224918_add_links.up.sql new file mode 100644 index 0000000..8d15324 --- /dev/null +++ b/migrations/20260326224918_add_links.up.sql @@ -0,0 +1,14 @@ +CREATE TABLE IF NOT EXISTS links ( + lid UUID PRIMARY KEY DEFAULT gen_random_uuid(), + endpoint_url TEXT NOT NULL UNIQUE, + dest_url TEXT NOT NULL, + oid UUID NOT NULL REFERENCES organizations(oid) ON DELETE CASCADE, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE IF NOT EXISTS link_visits ( + lvid UUID PRIMARY KEY DEFAULT gen_random_uuid(), + lid UUID NOT NULL REFERENCES links(lid) ON DELETE CASCADE, + uid UUID REFERENCES users(uid) ON DELETE SET NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); \ No newline at end of file diff --git a/tests/benchmarks/suite_test.go b/tests/benchmarks/suite_test.go index 3e8b8de..66f574b 100644 --- a/tests/benchmarks/suite_test.go +++ b/tests/benchmarks/suite_test.go @@ -48,7 +48,7 @@ func TestMain(m *testing.M) { log.Printf("Using schema from: %s", schemaPath) pgContainer, err := postgres.Run(ctx, - "postgres:18-alpine", + "postgres:16-alpine", postgres.WithInitScripts(schemaPath), postgres.WithDatabase("bench_db"), postgres.WithUsername("bench"), From 9dd7817629a80d4fdde7d244444de1258415ad87 Mon Sep 17 00:00:00 2001 From: Jeremy <87028711+jgoldberger26@users.noreply.github.com> Date: Thu, 26 Mar 2026 23:11:18 -0400 Subject: [PATCH 8/8] Revert schema.sql to use postgres 16 --- schema.sql | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/schema.sql b/schema.sql index 2ee9abf..e648e17 100644 --- a/schema.sql +++ b/schema.sql @@ -14,7 +14,7 @@ $$ language 'plpgsql'; -- 2. Tables CREATE TABLE IF NOT EXISTS users ( - uid UUID PRIMARY KEY DEFAULT uuidv4(), + uid UUID PRIMARY KEY DEFAULT gen_random_uuid(), first_name TEXT NOT NULL, last_name TEXT NOT NULL, personal_email TEXT UNIQUE, @@ -27,7 +27,7 @@ CREATE TABLE IF NOT EXISTS users ( ); CREATE TABLE IF NOT EXISTS organizations ( - oid UUID PRIMARY KEY DEFAULT uuidv4(), + oid UUID PRIMARY KEY DEFAULT gen_random_uuid(), name TEXT NOT NULL, date_created DATE DEFAULT CURRENT_DATE, date_modified DATE DEFAULT CURRENT_DATE @@ -43,7 +43,7 @@ CREATE TABLE IF NOT EXISTS org_members ( ); CREATE TABLE IF NOT EXISTS events ( - eid UUID PRIMARY KEY DEFAULT uuidv4(), + eid UUID PRIMARY KEY DEFAULT gen_random_uuid(), location TEXT, event_time TIMESTAMP, description TEXT, @@ -68,7 +68,7 @@ CREATE TABLE IF NOT EXISTS event_registrations ( -- 3. Bot Tokens (global access for M2M authentication) CREATE TABLE IF NOT EXISTS bot_tokens ( - token_id UUID PRIMARY KEY DEFAULT uuidv4(), + token_id UUID PRIMARY KEY DEFAULT gen_random_uuid(), token_hash TEXT NOT NULL, -- bcrypt hash of the token name TEXT NOT NULL, -- human-readable name for the bot created_by UUID NOT NULL REFERENCES users(uid), @@ -79,7 +79,7 @@ CREATE TABLE IF NOT EXISTS bot_tokens ( ); CREATE TABLE IF NOT EXISTS links ( - lid UUID PRIMARY KEY DEFAULT uuidv4(), + lid UUID PRIMARY KEY DEFAULT gen_random_uuid(), endpoint_url TEXT NOT NULL UNIQUE, dest_url TEXT NOT NULL, oid UUID NOT NULL REFERENCES organizations(oid) ON DELETE CASCADE, @@ -87,7 +87,7 @@ CREATE TABLE IF NOT EXISTS links ( ); CREATE TABLE IF NOT EXISTS link_visits ( - lvid UUID PRIMARY KEY DEFAULT uuidv7(), + lvid UUID PRIMARY KEY DEFAULT gen_random_uuid(), lid UUID NOT NULL REFERENCES links(lid) ON DELETE CASCADE, uid UUID REFERENCES users(uid) ON DELETE SET NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP