99 "fmt"
1010 "os"
1111 "path/filepath"
12+ "slices"
1213 "strings"
1314
1415 "code.gitea.io/gitea/modules/util"
@@ -19,20 +20,11 @@ import (
1920)
2021
2122type fixturesLoaderInternal struct {
22- engine * xorm.Engine
23- opts FixturesOptions
24- quoteObject func (string ) string
25- paramPlaceholder func (idx int ) string
26- }
27-
28- func (f * fixturesLoaderInternal ) prepareFieldValue (v any ) any {
29- if s , ok := v .(string ); ok {
30- if strings .HasPrefix (s , "0x" ) {
31- b , _ := hex .DecodeString (s [2 :])
32- return b
33- }
34- }
35- return v
23+ engine * xorm.Engine
24+ files []string
25+ cachedFixtureItems map [string ][]map [string ]any
26+ quoteObject func (string ) string
27+ paramPlaceholder func (idx int ) string
3628}
3729
3830func (f * fixturesLoaderInternal ) mssqlTableHasIdentityColumn (q * sql.Tx , tableName string ) (bool , error ) {
@@ -44,20 +36,39 @@ func (f *fixturesLoaderInternal) mssqlTableHasIdentityColumn(q *sql.Tx, tableNam
4436 return count > 0 , nil
4537}
4638
47- func (f * fixturesLoaderInternal ) loadFixtures (tx * sql.Tx , file string ) error {
48- data , err := os .ReadFile (file )
49- if err != nil {
50- return fmt .Errorf ("failed to read file %q: %w" , file , err )
39+ func (f * fixturesLoaderInternal ) preprocessFixtureItems (items []map [string ]any ) (err error ) {
40+ for _ , m := range items {
41+ for k , v := range m {
42+ if s , ok := v .(string ); ok {
43+ if strings .HasPrefix (s , "0x" ) {
44+ if m [k ], err = hex .DecodeString (s [2 :]); err != nil {
45+ return err
46+ }
47+ }
48+ }
49+ }
5150 }
51+ return nil
52+ }
5253
53- var fixtureItems []map [string ]any
54- if err := yaml .Unmarshal (data , & fixtureItems ); err != nil {
55- return fmt .Errorf ("failed to unmarshal yaml data from %q: %w" , file , err )
54+ func (f * fixturesLoaderInternal ) loadFixtures (tx * sql.Tx , file string ) error {
55+ fixtureItems , ok := f .cachedFixtureItems [file ]
56+ if ! ok {
57+ data , err := os .ReadFile (file )
58+ if err != nil {
59+ return fmt .Errorf ("failed to read file %q: %w" , file , err )
60+ }
61+ if err = yaml .Unmarshal (data , & fixtureItems ); err != nil {
62+ return fmt .Errorf ("failed to unmarshal yaml data from %q: %w" , file , err )
63+ }
64+ if err = f .preprocessFixtureItems (fixtureItems ); err != nil {
65+ return fmt .Errorf ("failed to preprocess fixture items from %q: %w" , file , err )
66+ }
67+ f .cachedFixtureItems [file ] = fixtureItems
5668 }
57-
5869 tableName , _ , _ := strings .Cut (filepath .Base (file ), "." )
5970 tableNameQuoted := f .quoteObject (tableName )
60- _ , err = tx .Exec (fmt .Sprintf ("DELETE FROM %s" , tableNameQuoted )) // sqlite3 doesn't support truncate
71+ _ , err : = tx .Exec (fmt .Sprintf ("DELETE FROM %s" , tableNameQuoted )) // sqlite3 doesn't support truncate
6172 if err != nil {
6273 return err
6374 }
@@ -84,7 +95,7 @@ func (f *fixturesLoaderInternal) loadFixtures(tx *sql.Tx, file string) error {
8495 for k , v := range item {
8596 sqlBuf = append (sqlBuf , f .quoteObject (k )... )
8697 sqlBuf = append (sqlBuf , "," ... )
87- sqlArguments = append (sqlArguments , f . prepareFieldValue ( v ) )
98+ sqlArguments = append (sqlArguments , v )
8899 }
89100 sqlBuf = sqlBuf [:len (sqlBuf )- 1 ]
90101 sqlBuf = append (sqlBuf , ") VALUES (" ... )
@@ -112,10 +123,6 @@ func (f *fixturesLoaderInternal) Load() error {
112123 case schemas .SQLITE :
113124 f .quoteObject = func (s string ) string { return fmt .Sprintf (`"%s"` , s ) }
114125 f .paramPlaceholder = func (idx int ) string { return "?" }
115- if _ , err := goDB .Exec ("PRAGMA defer_foreign_keys = ON" ); err != nil {
116- return err
117- }
118- defer func () { _ , _ = goDB .Exec ("PRAGMA defer_foreign_keys = OFF" ) }()
119126 case schemas .POSTGRES :
120127 f .quoteObject = func (s string ) string { return fmt .Sprintf (`"%s"` , s ) }
121128 f .paramPlaceholder = func (idx int ) string { return fmt .Sprintf (`$%d` , idx ) }
@@ -126,33 +133,36 @@ func (f *fixturesLoaderInternal) Load() error {
126133 f .quoteObject = func (s string ) string { return fmt .Sprintf ("[%s]" , s ) }
127134 f .paramPlaceholder = func (idx int ) string { return "?" }
128135 }
129- if len (f .opts .Files ) == 0 {
130- entries , err := os .ReadDir (f .opts .Dir )
131- if err != nil {
132- return err
133- }
134- for _ , e := range entries {
135- f .opts .Files = append (f .opts .Files , e .Name ())
136- }
137- }
138136
139137 tx , err := goDB .Begin ()
140138 if err != nil {
141139 return err
142140 }
143141 defer func () { _ = tx .Rollback () }()
144142
145- for _ , file := range f .opts .Files {
146- if ! filepath .IsAbs (file ) {
147- file = filepath .Join (f .opts .Dir , file )
148- }
143+ for _ , file := range f .files {
149144 if err := f .loadFixtures (tx , file ); err != nil {
150145 return fmt .Errorf ("failed to load fixtures from %s: %w" , file , err )
151146 }
152147 }
153148 return tx .Commit ()
154149}
155150
156- func newFixturesLoaderInternal (x * xorm.Engine , opts FixturesOptions ) * fixturesLoaderInternal {
157- return & fixturesLoaderInternal {engine : x , opts : opts }
151+ func NewFixturesLoaderInternal (x * xorm.Engine , opts FixturesOptions ) FixturesLoader {
152+ files := slices .Clone (opts .Files )
153+ if len (files ) == 0 {
154+ entries , err := os .ReadDir (opts .Dir )
155+ if err != nil {
156+ panic (fmt .Errorf ("failed to read dir %q: %w" , opts .Dir , err ))
157+ }
158+ for _ , e := range entries {
159+ files = append (files , e .Name ())
160+ }
161+ }
162+ for i , file := range files {
163+ if ! filepath .IsAbs (file ) {
164+ files [i ] = filepath .Join (opts .Dir , file )
165+ }
166+ }
167+ return & fixturesLoaderInternal {engine : x , files : files , cachedFixtureItems : map [string ][]map [string ]any {}}
158168}
0 commit comments