diff --git a/server/internal/core/filesystem.go b/server/internal/core/filesystem.go index eb0214b3..497c6b60 100644 --- a/server/internal/core/filesystem.go +++ b/server/internal/core/filesystem.go @@ -486,7 +486,42 @@ func (f filesystem) Copy(src Resource, target string, id uuid.UUID, recursive, o return } + var tree []db.ReadDirRow + if recursive && src.Dir { + var err error + tree, err = f.db.ReadDir(f.ctx, db.ReadDirParams{ID: src.ID, IncludeRoot: false, Recursive: true}) + if err != nil { + return Resource{}, err + } + } + create := make([]db.CreateResourcesParams, 0, len(tree)) copy := make(map[uuid.UUID]uuid.UUID) + ids := make(map[string]uuid.UUID) + if src.Dir { + ids[""] = id + } else { + copy[src.ID] = id + } + + for _, src := range tree { + id := uuid.New() + parent := ids[src.Path[0:strings.LastIndex(src.Path, "/")]] + + create = append(create, db.CreateResourcesParams{ + ID: id, + Parent: parent, + Name: src.Name, + Dir: src.Dir, + ContentType: src.ContentType, + ContentSha256: src.ContentSha256, + }) + + if src.Dir { + ids[src.Path] = id + } else { + copy[src.ID] = id + } + } e = f.db.WithTx(f.ctx, func(dbh *db.DbHandler) error { f := f.withDb(dbh) @@ -495,46 +530,12 @@ func (f filesystem) Copy(src Resource, target string, id uuid.UUID, recursive, o return err } } - var tree []db.ReadDirRow - if recursive && src.Dir { - var err error - tree, err = f.db.ReadDir(f.ctx, db.ReadDirParams{ID: src.ID, IncludeRoot: false, Recursive: true}) - if err != nil { - return err - } - } dest, err = f.CreateMemberResource(destParent, id, destName, src.Dir) if err != nil { return err } - if !src.Dir { - copy[src.ID] = id - } - - ids := make(map[string]uuid.UUID) - ids[""] = dest.ID - for _, src := range tree { - id := uuid.New() - parent := ids[src.Path[0:strings.LastIndex(src.Path, "/")]] - - if _, err := dbh.CreateResource(f.ctx, db.CreateResourceParams{ID: id, Parent: &parent, Name: src.Name, Dir: src.Dir}); err != nil { - if strings.Contains(err.Error(), "unique_member_resource_name") { - return ErrResourceNameConflict - } - if strings.Contains(err.Error(), "resources_pkey") { - return ErrResourceIDConflict - } - return err - } else { - if src.Dir { - ids[src.Path] = id - } else { - copy[src.ID] = id - } - } - } - - return nil + _, err := dbh.CreateResources(f.ctx, create) + return err }) if e == nil { diff --git a/server/internal/db/copyfrom.go b/server/internal/db/copyfrom.go new file mode 100644 index 00000000..415eaa1e --- /dev/null +++ b/server/internal/db/copyfrom.go @@ -0,0 +1,47 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.26.0 +// source: copyfrom.go + +package db + +import ( + "context" +) + +// iteratorForCreateResources implements pgx.CopyFromSource. +type iteratorForCreateResources struct { + rows []CreateResourcesParams + skippedFirstNextCall bool +} + +func (r *iteratorForCreateResources) Next() bool { + if len(r.rows) == 0 { + return false + } + if !r.skippedFirstNextCall { + r.skippedFirstNextCall = true + return true + } + r.rows = r.rows[1:] + return len(r.rows) > 0 +} + +func (r iteratorForCreateResources) Values() ([]interface{}, error) { + return []interface{}{ + r.rows[0].ID, + r.rows[0].Parent, + r.rows[0].Name, + r.rows[0].Dir, + r.rows[0].ContentType, + r.rows[0].ContentSha256, + }, nil +} + +func (r iteratorForCreateResources) Err() error { + return nil +} + +func (q *Queries) CreateResources(ctx context.Context, arg []CreateResourcesParams) (int64, error) { + return q.db.CopyFrom(ctx, []string{"resources"}, []string{"id", "parent", "name", "dir", "content_type", "content_sha256"}, &iteratorForCreateResources{rows: arg}) +} diff --git a/server/internal/db/db.go b/server/internal/db/db.go index 5b8c8f53..bcc185ff 100644 --- a/server/internal/db/db.go +++ b/server/internal/db/db.go @@ -15,6 +15,7 @@ type DBTX interface { Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error) Query(context.Context, string, ...interface{}) (pgx.Rows, error) QueryRow(context.Context, string, ...interface{}) pgx.Row + CopyFrom(ctx context.Context, tableName pgx.Identifier, columnNames []string, rowSrc pgx.CopyFromSource) (int64, error) } func New(db DBTX) *Queries { diff --git a/server/internal/db/resources.sql.go b/server/internal/db/resources.sql.go index 76b16411..00a4bc54 100644 --- a/server/internal/db/resources.sql.go +++ b/server/internal/db/resources.sql.go @@ -80,6 +80,15 @@ func (q *Queries) CreateResource(ctx context.Context, arg CreateResourceParams) return i, err } +type CreateResourcesParams struct { + ID uuid.UUID + Parent uuid.UUID + Name string + Dir bool + ContentType string + ContentSha256 string +} + const deleteRecursive = `-- name: DeleteRecursive :many WITH RECURSIVE nodes(id, parent) AS ( SELECT r.id, r.parent diff --git a/server/sql/queries/resources.sql b/server/sql/queries/resources.sql index b83b71f8..088fff1a 100644 --- a/server/sql/queries/resources.sql +++ b/server/sql/queries/resources.sql @@ -7,6 +7,13 @@ INSERT INTO resources( CASE @dir WhEN TRUE THEN '' ELSE 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' END ) RETURNING *; +-- name: CreateResources :copyfrom +INSERT INTO resources( + id, parent, name, dir, content_type, content_sha256 +) VALUES ( + @id::uuid, @parent::uuid, @name::text, @dir::boolean, @content_type::text, @content_sha256::text +); + -- name: UpdateResourceContents :exec UPDATE resources SET