From 4dc479ba9e067f868dfb76fda7c939543378708f Mon Sep 17 00:00:00 2001 From: Joakim Date: Tue, 9 Sep 2025 00:28:10 +0200 Subject: [PATCH] docs: clarify sqlc DDL support limitations and correct implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Discovery**: sqlc's DDL support is database-engine specific: ✅ **PostgreSQL**: sqlc generates functions for CREATE INDEX, CREATE FUNCTION ❌ **SQLite**: sqlc does NOT generate CREATE INDEX functions ❌ **Both**: sqlc does NOT generate CREATE TRIGGER functions **Corrected Implementation**: - Use sqlc-generated setup functions where available (tables always, PostgreSQL indexes) - Use manual SQL where sqlc doesn't support (SQLite indexes, all triggers) - Comments clarify why manual SQL is needed in each case **Architecture Principle**: Use sqlc for what it supports, supplement with manual SQL for what it doesn't - this is the recommended hybrid approach. --- TODO.md | 12 +++++++++++- insertr-server/insertr-server | Bin 13457464 -> 13457464 bytes insertr-server/internal/db/database.go | 12 ++++++------ 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/TODO.md b/TODO.md index 09bb2bc..8527b45 100644 --- a/TODO.md +++ b/TODO.md @@ -238,4 +238,14 @@ CREATE INDEX IF NOT EXISTS idx_content_site_id ON content(site_id); - Keep schema in separate `.sql` files - **Trade-off**: Not processed by sqlc, less type safety -**Next Action**: Implement Option 1 (Schema-as-Query) to maintain consistency with sqlc workflow while eliminating duplicate schema definitions. +**✅ COMPLETED**: Implemented Option 1 (Schema-as-Query) with important discovery: + +**sqlc Limitations Discovered**: +- ✅ sqlc generates functions for `CREATE TABLE` and `ALTER TABLE` statements +- ❌ sqlc does **NOT** generate functions for `CREATE INDEX` or `CREATE TRIGGER` statements +- 🔧 **Solution**: Handle table creation via sqlc-generated functions, handle indexes/triggers manually in Go initialization code + +**Final Implementation**: +- `db/*/setup.sql` - Contains table creation queries (sqlc generates type-safe functions) +- `internal/db/database.go` - Manual index/trigger creation using raw SQL +- **Best Practice**: Use sqlc for what it supports, manual SQL for what it doesn't diff --git a/insertr-server/insertr-server b/insertr-server/insertr-server index 8a89f52b323c66b8c8c38eebf81bc40b3a81de6d..4d4c7256f9f725fb76943e53030f4ee3e26a8e9c 100755 GIT binary patch delta 1139 zcmdn7VJjO5Z}`Amf6IsMkF{ZjqGqLM(}63k$xNAK|NE22<8HOSG|*f+={OgG3kBg-PbJT%@+KO#FY%*ot1BgC!PGT7L? zEZ@7rTi+;DS2v)@AjLc{KEB91%P_|?!+M87$or9Gd8u|1!KsXd>Cxjml+ z#Aj{KXJKp4XJK#8XW?kiXW?wmXW?qkXW?$oXW?njXW?znXW?tlXW?(pXAx-6XAx}A zXAx@8XAy4CXAx=7XAy1BXAx`9XAy7DXOU>nXOV2rXOU{pXOV8tXOU^oXOV5sXOU~q zXOVBuXHjU+XHjg=XHja;XHjm?XHjX-XHjj>XHjd&XYpyzXYp;%XYp&#XYp^(X9;M}X9;Z2X9;T0 zX9;f4X9?M!&k|~pDr{tAWNKipYiMX`o|s~6VPIlvxP4b<=mSO#WFbS7w8Yd@6O*)5 z%VZ;C<0O+51A`<(QCV3xICp@;PTXV zfy>j{1ujo-7q~p5UEuP}c7e;Y+66ApZWp*br(NLk+;)M>^V$V2&uL<1uidd7r4BlUEuP{c7e;Q+66AJZWp+`rd{Cj+IE4< z>)Hh_uWuK)yrEs-^2T<7%bVH-E^lrZxV)uZ;PTdXfy>+41uk!I7r4BmUEuQ0c7e;g z+66A}ZWp+`r(NLk-gbe@``QIA?{62le4t(6^1*h2%ZJ(pE+1|exO}8t;PTOSfy>9* f1uh?N7r1<)UEuP`c7e;M+66A3-Y#(Yj7BQ}pjM%i delta 1139 zcmdn7VJjO5Z}`AmZ<@P@K|#Pl)!g;><;62Lv75M03}9hkU0!EbaL$jP3a>OzrtB%CJ_~1iJ_}cSJ_~nyJ_}EKJ_~PqJ_}!aJ_~<)K8rwmK8s*` zK8sL$K8tXBK8r|uK8t93K8sj;K8tvJK8r+qK8s{~K8sX)K8tjFK8s9yK8tL7K8sv? zK8t*NK8r$oK8s>|K8sR&K8tdDK8s3wK8tF5K8sp=K8t#LK8r?sK8t31K8sd+K8tpH zK8sF!K8tR9K8s#^K8t>PK8rznK8s;{K8sO%K8taCK8s0vK8tC4K8smK8t&MK8r_tK8t62K8sg-K8tsIK8sI#K8tUAK8s&_K8t^QK1)D*K1*PGK1)!0 zK1*r5RczSsGcU zq!=b9rzV@G7^S9~8CfQ#BqvXQd#O^I!_eHy#K6kHa=O6fO6_)moX~cGoUnF*obYym zoQQUToXB>8oTzqzoalCeoS1fjoY;1OoVa#@ocMNuoP>6PoWyp4oTPSvoaA&3tX;g z7r0#8E^xW7UEp$kyTIj!c7e-{?E;sZ+668*w+mcuX&1QM+AeUptzF=9d%M8pj&^~| zo$UgbyV?aVcee{%?r9ge+}kd2xvyQ|a(}zP zUe_*gd40RUkmk+iJTt3t;aQSe%z~v+D0+)}r3tT?d fE^zsHyTIiW?E;riwhLT7)h=-P^mc*EXEa&?_5Y^^ diff --git a/insertr-server/internal/db/database.go b/insertr-server/internal/db/database.go index f998db5..eb8ded0 100644 --- a/insertr-server/internal/db/database.go +++ b/insertr-server/internal/db/database.go @@ -108,7 +108,7 @@ func (db *Database) initializeSQLiteSchema() error { return fmt.Errorf("failed to create content_versions table: %w", err) } - // Create indexes (manual for now since sqlc didn't generate them) + // Create indexes manually (sqlc doesn't generate CREATE INDEX functions for SQLite) indexQueries := []string{ "CREATE INDEX IF NOT EXISTS idx_content_site_id ON content(site_id);", "CREATE INDEX IF NOT EXISTS idx_content_updated_at ON content(updated_at);", @@ -121,7 +121,7 @@ func (db *Database) initializeSQLiteSchema() error { } } - // Create update trigger (manual for now) + // Create update trigger manually (sqlc doesn't generate trigger creation functions) triggerQuery := ` CREATE TRIGGER IF NOT EXISTS update_content_updated_at AFTER UPDATE ON content @@ -141,7 +141,7 @@ func (db *Database) initializeSQLiteSchema() error { func (db *Database) initializePostgreSQLSchema() error { ctx := context.Background() - // Create tables + // Create tables using sqlc-generated functions if err := db.postgresqlQueries.InitializeSchema(ctx); err != nil { return fmt.Errorf("failed to create content table: %w", err) } @@ -150,7 +150,7 @@ func (db *Database) initializePostgreSQLSchema() error { return fmt.Errorf("failed to create content_versions table: %w", err) } - // Create indexes using generated functions + // Create indexes using sqlc-generated functions (PostgreSQL supports this) if err := db.postgresqlQueries.CreateContentSiteIndex(ctx); err != nil { return fmt.Errorf("failed to create content site index: %w", err) } @@ -163,12 +163,12 @@ func (db *Database) initializePostgreSQLSchema() error { return fmt.Errorf("failed to create versions lookup index: %w", err) } - // Create update function and trigger + // Create update function using sqlc-generated function if err := db.postgresqlQueries.CreateUpdateFunction(ctx); err != nil { return fmt.Errorf("failed to create update function: %w", err) } - // Create trigger manually (sqlc didn't generate this) + // Create trigger manually (sqlc doesn't generate trigger creation functions) triggerQuery := ` DROP TRIGGER IF EXISTS update_content_updated_at ON content; CREATE TRIGGER update_content_updated_at