Skip to content

Commit 036cb1f

Browse files
committed
feat: use sqlc to write queries
1 parent 11a3627 commit 036cb1f

12 files changed

+1171
-0
lines changed

go.mod

+3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ require (
1414
github.com/goto/salt v0.3.3
1515
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0
1616
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0
17+
github.com/jackc/pgx/v5 v5.4.3
1718
github.com/jmoiron/sqlx v1.3.5
1819
github.com/lib/pq v1.10.9
1920
github.com/mcuadros/go-defaults v1.2.0
@@ -202,6 +203,8 @@ require (
202203
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
203204
github.com/hashicorp/errwrap v1.1.0 // indirect
204205
github.com/hashicorp/go-multierror v1.1.1 // indirect
206+
github.com/jackc/pgpassfile v1.0.0 // indirect
207+
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
205208
github.com/leodido/go-urn v1.2.4 // indirect
206209
github.com/newrelic/csec-go-agent v0.4.0 // indirect
207210
github.com/newrelic/newrelic-telemetry-sdk-go v0.8.1 // indirect

go.sum

+5
Original file line numberDiff line numberDiff line change
@@ -912,6 +912,7 @@ github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfG
912912
github.com/jackc/pgerrcode v0.0.0-20201024163028-a0d42d470451/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds=
913913
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
914914
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
915+
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
915916
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
916917
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
917918
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
@@ -923,6 +924,8 @@ github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwX
923924
github.com/jackc/pgproto3/v2 v2.0.7/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
924925
github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
925926
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
927+
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
928+
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
926929
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
927930
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
928931
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
@@ -937,6 +940,8 @@ github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXg
937940
github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o=
938941
github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg=
939942
github.com/jackc/pgx/v4 v4.10.1/go.mod h1:QlrWebbs3kqEZPHCTGyxecvzG6tvIsYu+A5b1raylkA=
943+
github.com/jackc/pgx/v5 v5.4.3 h1:cxFyXhxlvAifxnkKKdlxv8XqUf59tDlYjnV5YYfsJJY=
944+
github.com/jackc/pgx/v5 v5.4.3/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA=
940945
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
941946
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
942947
github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=

internal/store/pgsql/modules.go

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package pgsql
2+
3+
import (
4+
"context"
5+
6+
"github.com/goto/entropy/core/module"
7+
"github.com/goto/entropy/internal/store/pgsql/queries"
8+
)
9+
10+
func (st *Store) GetModule(ctx context.Context, urn string) (*module.Module, error) {
11+
mod, err := st.qu.GetModuleByURN(ctx, urn)
12+
if err != nil {
13+
return nil, translateSQLErr(err)
14+
}
15+
16+
return &module.Module{
17+
URN: mod.Urn,
18+
Name: mod.Name,
19+
Project: mod.Project,
20+
Configs: mod.Configs,
21+
CreatedAt: mod.CreatedAt.Time,
22+
UpdatedAt: mod.UpdatedAt.Time,
23+
}, nil
24+
}
25+
26+
func (st *Store) ListModules(ctx context.Context, project string) ([]module.Module, error) {
27+
mods, err := st.qu.ListAllModulesForProject(ctx, project)
28+
if err != nil {
29+
return nil, translateSQLErr(err)
30+
}
31+
32+
var modules []module.Module
33+
for _, mod := range mods {
34+
modules = append(modules, module.Module{
35+
URN: mod.Urn,
36+
Name: mod.Name,
37+
Project: mod.Project,
38+
Configs: mod.Configs,
39+
CreatedAt: mod.CreatedAt.Time,
40+
UpdatedAt: mod.UpdatedAt.Time,
41+
})
42+
}
43+
44+
return modules, nil
45+
}
46+
47+
func (st *Store) CreateModule(ctx context.Context, m module.Module) error {
48+
params := queries.InsertModuleParams{
49+
Urn: m.URN,
50+
Project: m.Project,
51+
Name: m.Name,
52+
Configs: m.Configs,
53+
}
54+
55+
if err := st.qu.InsertModule(ctx, params); err != nil {
56+
return translateSQLErr(err)
57+
}
58+
return nil
59+
}
60+
61+
func (st *Store) UpdateModule(ctx context.Context, m module.Module) error {
62+
params := queries.UpdateModuleParams{
63+
Urn: m.URN,
64+
Configs: m.Configs,
65+
}
66+
if err := st.qu.UpdateModule(ctx, params); err != nil {
67+
return translateSQLErr(err)
68+
}
69+
return nil
70+
}
71+
72+
func (st *Store) DeleteModule(ctx context.Context, urn string) error {
73+
if err := st.qu.DeleteModule(ctx, urn); err != nil {
74+
return translateSQLErr(err)
75+
}
76+
return nil
77+
}

internal/store/pgsql/pgsql.go

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package pgsql
2+
3+
import (
4+
"context"
5+
"database/sql"
6+
"fmt"
7+
"strings"
8+
"time"
9+
10+
"github.com/jackc/pgx/v5"
11+
"github.com/lib/pq"
12+
13+
_ "github.com/jackc/pgx/v5"
14+
15+
"github.com/goto/entropy/core/resource"
16+
"github.com/goto/entropy/internal/store/pgsql/queries"
17+
"github.com/goto/entropy/pkg/errors"
18+
)
19+
20+
//go:generate sqlc generate
21+
22+
type Store struct {
23+
qu *queries.Queries
24+
pgx *pgx.Conn
25+
}
26+
27+
func (st *Store) Close() error { return st.pgx.Close(context.Background()) }
28+
29+
// Open returns store instance backed by PostgresQL.
30+
func Open(conStr string, refreshInterval, extendInterval time.Duration) (*Store, error) {
31+
conn, err := pgx.Connect(context.Background(), conStr)
32+
if err != nil {
33+
return nil, err
34+
} else if err := conn.Ping(context.Background()); err != nil {
35+
_ = conn.Close(context.Background())
36+
return nil, err
37+
}
38+
39+
if refreshInterval >= extendInterval {
40+
return nil, errors.New("refreshInterval must be lower than extendInterval")
41+
}
42+
43+
return &Store{
44+
qu: queries.New(conn),
45+
pgx: conn,
46+
}, nil
47+
}
48+
49+
func translateSQLErr(err error) error {
50+
if errors.Is(err, sql.ErrNoRows) {
51+
return errors.ErrNotFound.WithCausef(err.Error())
52+
}
53+
54+
var pgErr *pq.Error
55+
if errors.As(err, &pgErr) {
56+
// Refer http://www.postgresql.org/docs/9.3/static/errcodes-appendix.html
57+
switch pgErr.Code.Name() {
58+
case "unique_violation":
59+
return errors.ErrConflict.WithCausef(err.Error())
60+
61+
default:
62+
return errors.ErrInternal.WithCausef(err.Error())
63+
}
64+
}
65+
66+
return err
67+
}
68+
69+
func tagsToLabelMap(tags []string) map[string]string {
70+
const keyValueParts = 2
71+
72+
labels := map[string]string{}
73+
for _, tag := range tags {
74+
parts := strings.SplitN(tag, "=", keyValueParts)
75+
key, val := parts[0], parts[1]
76+
labels[key] = val
77+
}
78+
return labels
79+
}
80+
81+
func labelToTag(k, v string) string {
82+
return fmt.Sprintf("%s=%s", k, v)
83+
}
84+
85+
type TxFunc func(ctx context.Context, tx pgx.Tx) error
86+
87+
func withinTx(ctx context.Context, db *pgx.Conn, readOnly bool, fns ...TxFunc) error {
88+
var opts pgx.TxOptions
89+
if readOnly {
90+
opts.AccessMode = pgx.ReadOnly
91+
} else {
92+
opts.AccessMode = pgx.ReadWrite
93+
}
94+
95+
tx, err := db.BeginTx(ctx, opts)
96+
if err != nil {
97+
return err
98+
}
99+
100+
for _, fn := range fns {
101+
if err := fn(ctx, tx); err != nil {
102+
_ = tx.Rollback(ctx)
103+
return err
104+
}
105+
}
106+
107+
return tx.Commit(ctx)
108+
}
109+
110+
func runAllHooks(ctx context.Context, hooks []resource.MutationHook) error {
111+
for _, hook := range hooks {
112+
if err := hook(ctx); err != nil {
113+
return err
114+
}
115+
}
116+
return nil
117+
}

internal/store/pgsql/queries.sql

+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
-- name: GetModuleByURN :one
2+
SELECT *
3+
FROM modules
4+
WHERE urn = $1;
5+
6+
-- name: ListAllModulesForProject :many
7+
SELECT *
8+
FROM modules
9+
WHERE project = $1;
10+
11+
-- name: InsertModule :exec
12+
INSERT INTO modules (urn, project, name, configs)
13+
VALUES ($1, $2, $3, $4);
14+
15+
-- name: UpdateModule :exec
16+
UPDATE modules
17+
SET configs = $2,
18+
updated_at = current_timestamp
19+
WHERE urn = $1;
20+
21+
-- name: DeleteModule :exec
22+
DELETE
23+
FROM modules
24+
WHERE urn = $1;
25+
26+
-- name: GetResourceByURN :one
27+
SELECT r.*,
28+
array_agg(rt.tag)::text[] AS tags,
29+
(CASE
30+
WHEN COUNT(rd.dependency_key) > 0 THEN
31+
json_object_agg(rd.dependency_key, d.urn)
32+
ELSE
33+
'{}'::json
34+
END) AS dependencies
35+
FROM resources r
36+
LEFT JOIN resource_tags rt ON r.id = rt.resource_id
37+
LEFT JOIN resource_dependencies rd ON r.id = rd.resource_id
38+
LEFT JOIN resources d ON rd.depends_on = d.id
39+
WHERE r.urn = $1
40+
GROUP BY r.id;
41+
42+
-- name: GetResourceDependencies :one
43+
SELECT (CASE
44+
WHEN COUNT(rd.dependency_key) > 0 THEN
45+
json_object_agg(rd.dependency_key, d.urn)
46+
ELSE
47+
'{}'::json
48+
END) AS dependencies
49+
FROM resources r
50+
LEFT JOIN resource_dependencies rd ON r.id = rd.resource_id
51+
LEFT JOIN resources d ON rd.depends_on = d.id
52+
WHERE r.urn = $1
53+
GROUP BY r.id;
54+
55+
-- name: ListResourceURNsByFilter :many
56+
SELECT r.*,
57+
array_agg(rt.tag)::text[] AS tags
58+
FROM resources r
59+
JOIN resource_tags rt ON r.id = rt.resource_id
60+
WHERE (sqlc.narg('project')::text IS NULL OR r.project = sqlc.narg('project'))
61+
AND (sqlc.narg('kind')::text IS NULL OR r.kind = sqlc.narg('kind'))
62+
GROUP BY r.id;
63+
64+
-- name: DeleteResourceDependenciesByURN :exec
65+
DELETE
66+
FROM resource_dependencies
67+
WHERE resource_id = (SELECT id FROM resources WHERE urn = $1);
68+
69+
-- name: DeleteResourceTagsByURN :exec
70+
DELETE
71+
FROM resource_tags
72+
WHERE resource_id = (SELECT id FROM resources WHERE urn = $1);
73+
74+
-- name: DeleteResourceByURN :exec
75+
DELETE
76+
FROM resources
77+
WHERE urn = $1;
78+
79+
-- name: ListResourceRevisions :many
80+
SELECT rev.*, array_agg(distinct rt.tag)::text[] AS tags
81+
FROM resources r
82+
JOIN revisions rev ON r.id = rev.resource_id
83+
JOIN revision_tags rt ON rev.id = rt.revision_id
84+
WHERE r.urn = $1
85+
GROUP BY rev.id;
86+
87+
88+
-- name: InsertResource :one
89+
INSERT INTO resources ("urn", "kind", "project", "name", "created_at", "updated_at", "created_by", "updated_by",
90+
"spec_configs", "state_status", "state_output", "state_module_data",
91+
"state_next_sync", "state_sync_result")
92+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
93+
RETURNING id;
94+
95+
-- name: InsertResourceTags :copyfrom
96+
INSERT INTO resource_tags (resource_id, tag)
97+
VALUES ($1, $2);
98+
99+
-- name: InsertResourceDependency :exec
100+
INSERT INTO resource_dependencies (resource_id, dependency_key, depends_on)
101+
VALUES ($1, $2, (SELECT id FROM resources WHERE urn = $3));

internal/store/pgsql/queries/copyfrom.go

+43
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)