Skip to content

Commit 12e573f

Browse files
committed
feat: implement CLI application with config and catalog loading
- Add main CLI application using urfave/cli/v3 with config-file and catalog-file flags - Implement Config struct and LoadConfig function for parsing sqlc.yaml files - Implement Catalog struct and LoadCatalog function for parsing JSON schema files - Add comprehensive test coverage for both config and catalog functionality - Include test data files (config_test.yaml, catalog_test.json) for validation - Add Generator struct with basic Generate method skeleton - Update integration tests to use environment variables for test files - Add dependencies: urfave/cli/v3 and gopkg.in/yaml.v3 Signed-off-by: Svetlin Ralchev <iamralch@users.noreply.github.com>
1 parent 5d3f4ca commit 12e573f

12 files changed

Lines changed: 506 additions & 15 deletions

File tree

‎cmd/sqlc-gen-queries/main.go‎

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,63 @@
11
package main
22

3+
import (
4+
"context"
5+
"log/slog"
6+
"os"
7+
"runtime/debug"
8+
9+
"github.com/sqlc-contrib/sqlc-gen-queries/internal/sqlc"
10+
"github.com/urfave/cli/v3"
11+
)
12+
313
func main() {
14+
app := &cli.Command{
15+
Name: "sqlc-gen-queries",
16+
Usage: "SQLC Queries Generator",
17+
UsageText: "sqlc-gen-queries [global options] [command]",
18+
Flags: []cli.Flag{
19+
&cli.StringFlag{
20+
Name: "config-file",
21+
Usage: "Path to the sqlc configuration file.",
22+
Sources: cli.EnvVars("SQLC_CONFIG_FILE"),
23+
Value: "sqlc.yaml",
24+
},
25+
&cli.StringFlag{
26+
Name: "catalog-file",
27+
Usage: "Path to the catalog file.",
28+
Sources: cli.EnvVars("SQLC_CATALOG_FILE"),
29+
Value: "schema.json",
30+
},
31+
},
32+
ErrWriter: os.Stderr,
33+
Action: func(ctx context.Context, cmd *cli.Command) error {
34+
config, err := sqlc.LoadConfig(cmd.String("config-file"))
35+
if err != nil {
36+
return err
37+
}
38+
39+
catalog, err := sqlc.LoadCatalog(cmd.String("catalog-file"))
40+
if err != nil {
41+
return err
42+
}
43+
44+
generator := &sqlc.Generator{
45+
Config: config,
46+
Catalog: catalog,
47+
}
48+
49+
return generator.Generate()
50+
},
51+
}
52+
53+
if info, ok := debug.ReadBuildInfo(); ok {
54+
app.Version = info.Main.Version
55+
}
56+
57+
ctx := context.Background()
58+
// start the application
59+
if err := app.Run(ctx, os.Args); err != nil {
60+
slog.ErrorContext(ctx, "The generator has encountered a fatal error", slog.Any("error", err))
61+
os.Exit(1)
62+
}
463
}

‎go.mod‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ tool (
1010
require (
1111
github.com/onsi/ginkgo/v2 v2.27.2
1212
github.com/onsi/gomega v1.38.2
13+
github.com/urfave/cli/v3 v3.6.1
14+
gopkg.in/yaml.v3 v3.0.1
1315
)
1416

1517
require (
@@ -94,7 +96,7 @@ require (
9496
github.com/google/go-github/v67 v67.0.0 // indirect
9597
github.com/google/go-github/v75 v75.0.0 // indirect
9698
github.com/google/go-querystring v1.1.0 // indirect
97-
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect
99+
github.com/google/pprof v0.0.0-20251114195745-4902fdda35c8 // indirect
98100
github.com/google/s2a-go v0.1.9 // indirect
99101
github.com/google/uuid v1.6.0 // indirect
100102
github.com/googleapis/enterprise-certificate-proxy v0.3.7 // indirect
@@ -156,11 +158,11 @@ require (
156158
go.opentelemetry.io/contrib/detectors/gcp v1.36.0 // indirect
157159
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect
158160
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
159-
go.opentelemetry.io/otel v1.37.0 // indirect
160-
go.opentelemetry.io/otel/metric v1.37.0 // indirect
161+
go.opentelemetry.io/otel v1.38.0 // indirect
162+
go.opentelemetry.io/otel/metric v1.38.0 // indirect
161163
go.opentelemetry.io/otel/sdk v1.37.0 // indirect
162164
go.opentelemetry.io/otel/sdk/metric v1.37.0 // indirect
163-
go.opentelemetry.io/otel/trace v1.37.0 // indirect
165+
go.opentelemetry.io/otel/trace v1.38.0 // indirect
164166
go.yaml.in/yaml/v3 v3.0.4 // indirect
165167
golang.org/x/crypto v0.45.0 // indirect
166168
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 // indirect
@@ -183,5 +185,4 @@ require (
183185
google.golang.org/protobuf v1.36.10 // indirect
184186
gopkg.in/ini.v1 v1.67.0 // indirect
185187
gopkg.in/warnings.v0 v0.1.2 // indirect
186-
gopkg.in/yaml.v3 v3.0.1 // indirect
187188
)

‎go.sum‎

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1548,8 +1548,8 @@ github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLe
15481548
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
15491549
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
15501550
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
1551-
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=
1552-
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
1551+
github.com/google/pprof v0.0.0-20251114195745-4902fdda35c8 h1:3DsUAV+VNEQa2CUVLxCY3f87278uWfIDhJnbdvDjvmE=
1552+
github.com/google/pprof v0.0.0-20251114195745-4902fdda35c8/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U=
15531553
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
15541554
github.com/google/s2a-go v0.1.0/go.mod h1:OJpEgntRZo8ugHpF9hkoLJbS5dSI20XZeXJ9JVywLlM=
15551555
github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
@@ -1803,8 +1803,8 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
18031803
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
18041804
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
18051805
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
1806-
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
1807-
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
1806+
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
1807+
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
18081808
github.com/tenntenn/golden v0.5.5 h1:3LPemp4rZUIYeh0MTgIn8nzbY8EKVu0Kyb7AOBM016g=
18091809
github.com/tenntenn/golden v0.5.5/go.mod h1:zPPkSkshkDGyYIFOIRkyydYQB4XErvg+Uufi0Q9H5qE=
18101810
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
@@ -1815,6 +1815,8 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
18151815
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
18161816
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
18171817
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
1818+
github.com/urfave/cli/v3 v3.6.1 h1:j8Qq8NyUawj/7rTYdBGrxcH7A/j7/G8Q5LhWEW4G3Mo=
1819+
github.com/urfave/cli/v3 v3.6.1/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
18181820
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
18191821
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
18201822
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
@@ -1862,23 +1864,23 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6h
18621864
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
18631865
go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY=
18641866
go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo=
1865-
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
1866-
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
1867+
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
1868+
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
18671869
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 h1:rixTyDGXFxRy1xzhKrotaHy3/KXdPhlWARrCgK+eqUY=
18681870
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0/go.mod h1:dowW6UsM9MKbJq5JTz2AMVp3/5iW5I/TStsk8S+CfHw=
18691871
go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8=
18701872
go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM=
1871-
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
1872-
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
1873+
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
1874+
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
18731875
go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A=
18741876
go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
18751877
go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
18761878
go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=
18771879
go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
18781880
go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo=
18791881
go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ=
1880-
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
1881-
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
1882+
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
1883+
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
18821884
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
18831885
go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
18841886
go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=

‎integration/suite_test.go‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,7 @@ var _ = BeforeSuite(func() {
2424
Expect(path).NotTo(BeEmpty())
2525
// prepare the command
2626
command = exec.Command(path)
27+
command.Env = append(command.Env, "SQLC_CONFIG_FILE=../internal/sqlc/config_test.yaml")
28+
command.Env = append(command.Env, "SQLC_CATALOG_FILE=../internal/sqlc/catalog_test.json")
2729
})
2830
})

‎internal/sqlc/catalog.go‎

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// Package sqlc provides data structures for working with sqlc catalog files.
2+
//
3+
// The catalog represents the database schema metadata generated by sqlc,
4+
// including schemas, tables, columns, indexes, and foreign key relationships.
5+
package sqlc
6+
7+
import (
8+
"encoding/json"
9+
"os"
10+
)
11+
12+
// Catalog represents the root structure of a sqlc catalog file.
13+
// It contains all schemas and their associated database objects.
14+
type Catalog struct {
15+
Schemas []Schema `json:"schemas,omitempty"`
16+
}
17+
18+
// LoadCatalog loads a sqlc catalog file from the specified path and returns a Catalog struct.
19+
// The catalog file is expected to be in JSON format as generated by sqlc.
20+
func LoadCatalog(path string) (*Catalog, error) {
21+
data, err := os.ReadFile(path)
22+
if err != nil {
23+
return nil, err
24+
}
25+
26+
var catalog Catalog
27+
if err := json.Unmarshal(data, &catalog); err != nil {
28+
return nil, err
29+
}
30+
31+
return &catalog, nil
32+
}
33+
34+
// Schema represents a database schema containing tables and other database objects.
35+
type Schema struct {
36+
Name string `json:"name"`
37+
Tables []Table `json:"tables,omitempty"`
38+
Attrs
39+
}
40+
41+
// Table represents a database table with its columns, indexes, and constraints.
42+
type Table struct {
43+
Name string `json:"name"`
44+
Columns []Column `json:"columns,omitempty"`
45+
Indexes []Index `json:"indexes,omitempty"`
46+
PrimaryKey *Index `json:"primary_key,omitempty"`
47+
ForeignKeys []ForeignKey `json:"foreign_keys,omitempty"`
48+
Attrs
49+
}
50+
51+
// Column represents a table column with its name, data type, and nullability.
52+
type Column struct {
53+
Name string `json:"name"`
54+
Type string `json:"type,omitempty"`
55+
Null bool `json:"null,omitempty"`
56+
Attrs
57+
}
58+
59+
// Index represents a database index on one or more columns or expressions.
60+
type Index struct {
61+
Name string `json:"name,omitempty"`
62+
Unique bool `json:"unique,omitempty"`
63+
Parts []IndexPart `json:"parts,omitempty"`
64+
}
65+
66+
// IndexPart represents a single part of an index, which can be either a column or an expression.
67+
type IndexPart struct {
68+
Desc bool `json:"desc,omitempty"`
69+
Column string `json:"column,omitempty"`
70+
Expr string `json:"expr,omitempty"`
71+
}
72+
73+
// ForeignKey represents a foreign key constraint linking columns in this table
74+
// to columns in a referenced table.
75+
type ForeignKey struct {
76+
Name string `json:"name"`
77+
Columns []string `json:"columns,omitempty"`
78+
References struct {
79+
Table string `json:"table"`
80+
Columns []string `json:"columns,omitempty"`
81+
} `json:"references"`
82+
}
83+
84+
// Attrs represents common attributes that can be applied to schemas, tables, and columns.
85+
// These are typically dialect-specific metadata.
86+
type Attrs struct {
87+
Comment string `json:"comment,omitempty"`
88+
Charset string `json:"charset,omitempty"`
89+
Collate string `json:"collate,omitempty"`
90+
}

‎internal/sqlc/catalog_test.go‎

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package sqlc_test
2+
3+
import (
4+
"github.com/sqlc-contrib/sqlc-gen-queries/internal/sqlc"
5+
6+
. "github.com/onsi/ginkgo/v2"
7+
. "github.com/onsi/gomega"
8+
)
9+
10+
var _ = Describe("Catalog", func() {
11+
Describe("LoadCatalog", func() {
12+
It("loads and parses a complete catalog file correctly", func() {
13+
catalog, err := sqlc.LoadCatalog("./catalog_test.json")
14+
Expect(err).NotTo(HaveOccurred())
15+
Expect(catalog).NotTo(BeNil())
16+
17+
// Verify schemas
18+
Expect(catalog.Schemas).To(HaveLen(2))
19+
Expect(catalog.Schemas[0].Name).To(Equal("public"))
20+
Expect(catalog.Schemas[1].Name).To(Equal("analytics"))
21+
22+
// Verify schema attributes
23+
publicSchema := catalog.Schemas[0]
24+
Expect(publicSchema.Comment).To(Equal("Public schema"))
25+
26+
// Verify tables
27+
Expect(publicSchema.Tables).To(HaveLen(2))
28+
Expect(publicSchema.Tables[0].Name).To(Equal("users"))
29+
Expect(publicSchema.Tables[1].Name).To(Equal("posts"))
30+
31+
// Verify users table
32+
usersTable := publicSchema.Tables[0]
33+
Expect(usersTable.Comment).To(Equal("User accounts"))
34+
35+
// Verify columns
36+
Expect(usersTable.Columns).To(HaveLen(3))
37+
Expect(usersTable.Columns[0].Name).To(Equal("id"))
38+
Expect(usersTable.Columns[0].Type).To(Equal("integer"))
39+
Expect(usersTable.Columns[0].Null).To(BeFalse())
40+
Expect(usersTable.Columns[0].Comment).To(Equal("Primary key"))
41+
42+
Expect(usersTable.Columns[1].Name).To(Equal("email"))
43+
Expect(usersTable.Columns[1].Type).To(Equal("varchar(255)"))
44+
Expect(usersTable.Columns[1].Null).To(BeFalse())
45+
46+
Expect(usersTable.Columns[2].Name).To(Equal("name"))
47+
Expect(usersTable.Columns[2].Type).To(Equal("text"))
48+
Expect(usersTable.Columns[2].Null).To(BeTrue())
49+
50+
// Verify primary key
51+
Expect(usersTable.PrimaryKey).NotTo(BeNil())
52+
Expect(usersTable.PrimaryKey.Name).To(Equal("users_pkey"))
53+
Expect(usersTable.PrimaryKey.Unique).To(BeTrue())
54+
Expect(usersTable.PrimaryKey.Parts).To(HaveLen(1))
55+
Expect(usersTable.PrimaryKey.Parts[0].Column).To(Equal("id"))
56+
Expect(usersTable.PrimaryKey.Parts[0].Desc).To(BeFalse())
57+
58+
// Verify indexes
59+
Expect(usersTable.Indexes).To(HaveLen(1))
60+
emailIndex := usersTable.Indexes[0]
61+
Expect(emailIndex.Name).To(Equal("idx_users_email"))
62+
Expect(emailIndex.Unique).To(BeTrue())
63+
Expect(emailIndex.Parts).To(HaveLen(1))
64+
Expect(emailIndex.Parts[0].Column).To(Equal("email"))
65+
66+
// Verify posts table
67+
postsTable := publicSchema.Tables[1]
68+
69+
// Verify foreign keys
70+
Expect(postsTable.ForeignKeys).To(HaveLen(1))
71+
fk := postsTable.ForeignKeys[0]
72+
Expect(fk.Name).To(Equal("fk_posts_user_id"))
73+
Expect(fk.Columns).To(Equal([]string{"user_id"}))
74+
Expect(fk.References.Table).To(Equal("users"))
75+
Expect(fk.References.Columns).To(Equal([]string{"id"}))
76+
77+
// Verify expression-based indexes
78+
Expect(postsTable.Indexes).To(HaveLen(2))
79+
exprIndex := postsTable.Indexes[1]
80+
Expect(exprIndex.Name).To(Equal("idx_posts_title_expr"))
81+
Expect(exprIndex.Parts).To(HaveLen(1))
82+
Expect(exprIndex.Parts[0].Expr).To(Equal("lower(title)"))
83+
Expect(exprIndex.Parts[0].Column).To(BeEmpty())
84+
})
85+
86+
When("the file does not exist", func() {
87+
It("returns an error", func() {
88+
catalog, err := sqlc.LoadCatalog("./catalog_test.yaml")
89+
Expect(err).To(MatchError(ContainSubstring("no such file or directory")))
90+
Expect(catalog).To(BeNil())
91+
})
92+
})
93+
94+
When("the file is invalid JSON", func() {
95+
It("returns an error", func() {
96+
catalog, err := sqlc.LoadCatalog("./catalog.go")
97+
Expect(err).To(HaveOccurred())
98+
Expect(catalog).To(BeNil())
99+
})
100+
})
101+
})
102+
})

0 commit comments

Comments
 (0)