~evanj/cms

c00eee12da34c8c9d1352fa12e43b580fb22fea1 — Evan M Jones 5 months ago 051b02d
Feat(copy): Space copier refactor. Improved iterators + copy
transaction. General DB cleanup along the way.
3 files changed, 135 insertions(+), 99 deletions(-)

M internal/s/db/content.go
M internal/s/db/contenttype.go
M internal/s/db/space.go
M internal/s/db/content.go => internal/s/db/content.go +48 -32
@@ 414,7 414,6 @@ func (db *DB) valueReferenceListUpdate(s space.Space, ct contenttype.ContentType
	// TODO: Remove these inline queries, please.
	var refListID string
	if err := t.QueryRow("SELECT cms_value_reference_list.ID FROM cms_value JOIN cms_value_reference_list ON cms_value_reference_list.ID = cms_value.VALUE_ID WHERE cms_value.ID = ?", valueID).Scan(&refListID); err != nil {
		db.log.Println("valueID", valueID, "Error", err)
		return fmt.Errorf("failed to query for reference list identifier before reference list update")
	}



@@ 447,7 446,6 @@ func (db *DB) valueReferenceListNew(s space.Space, ct contenttype.ContentType, c

	res, err := t.Exec("INSERT INTO cms_value_reference_list () VALUES ();")
	if err != nil {
		db.log.Println(err)
		return fmt.Errorf("failed to attach field value of content")
	}



@@ 458,7 456,6 @@ func (db *DB) valueReferenceListNew(s space.Space, ct contenttype.ContentType, c

	res, err = t.Exec(queryValueNew, c.ID(), ct.ID(), fieldName, refListID)
	if err != nil {
		db.log.Println(err)
		return fmt.Errorf("failed to attach field value of content")
	}



@@ 485,13 482,7 @@ func (db *DB) valueReferenceListNew(s space.Space, ct contenttype.ContentType, c
	return nil
}

func (db *DB) contentNew(space space.Space, ct contenttype.ContentType, params []ContentNewParam, depth int) (content.Content, error) {
	t, err := db.Begin()
	if err != nil {
		return nil, err
	}
	defer t.Rollback()

func (db *DB) contentNew(t *sql.Tx, space space.Space, ct contenttype.ContentType, params []ContentNewParam, depth int) (content.Content, error) {
	res, err := t.Exec(queryContentNew, ct.ID())
	if err != nil {
		return nil, fmt.Errorf("failed to create new content attached to contenttype of '%s'", ct.Name())


@@ 523,7 514,6 @@ func (db *DB) contentNew(space space.Space, ct contenttype.ContentType, params [

		res, err := t.Exec(queryValueNewType, item.Value)
		if err != nil {
			db.log.Println(err)
			return nil, fmt.Errorf("failed to attach field value of content")
		}



@@ 534,7 524,6 @@ func (db *DB) contentNew(space space.Space, ct contenttype.ContentType, params [

		res, err = t.Exec(queryValueNew, contentID, ct.ID(), item.Name, valueID)
		if err != nil {
			db.log.Println(err)
			return nil, fmt.Errorf("failed to attach field value of content")
		}



@@ 559,10 548,22 @@ func (db *DB) contentNew(space space.Space, ct contenttype.ContentType, params [
		return nil, fmt.Errorf("failed to create all values")
	}

	return &content, t.Commit()
	return &content, nil
}

func (db *DB) ContentNew(space space.Space, ct contenttype.ContentType, params []ContentNewParam) (content.Content, error) {
	return db.contentNew(space, ct, params, defaultDepth)
	t, err := db.Begin()
	if err != nil {
		return nil, err
	}
	defer t.Rollback()

	c, err := db.contentNew(t, space, ct, params, defaultDepth)
	if err != nil {
		return nil, err
	}

	return c, t.Commit()
}

func (db *DB) ContentUpdate(space space.Space, ct contenttype.ContentType, content content.Content, newParams []ContentNewParam, updateParams []ContentUpdateParam) (content.Content, error) {


@@ 597,7 598,6 @@ func (db *DB) ContentUpdate(space space.Space, ct contenttype.ContentType, conte
		}

		if _, err := t.Exec(queryValueUpdate, item.Value, item.ID); err != nil {
			db.log.Println(err)
			return nil, fmt.Errorf("failed to create update content value '%s'", item.Value)
		}
	}


@@ 618,7 618,6 @@ func (db *DB) ContentUpdate(space space.Space, ct contenttype.ContentType, conte

		res, err := t.Exec(queryValueNewType, item.Value)
		if err != nil {
			db.log.Println(err)
			return nil, fmt.Errorf("failed to attach field value of content")
		}



@@ 629,7 628,6 @@ func (db *DB) ContentUpdate(space space.Space, ct contenttype.ContentType, conte

		res, err = t.Exec(queryValueNew, content.ID(), ct.ID(), item.Name, valueID)
		if err != nil {
			db.log.Println(err)
			return nil, fmt.Errorf("failed to attach field value of content")
		}



@@ 662,7 660,6 @@ func (db *DB) ContentDelete(space space.Space, ct contenttype.ContentType, conte
	if err != nil {
		return err
	}

	defer t.Rollback()

	for _, q := range queryContentDelete {


@@ 672,11 669,7 @@ func (db *DB) ContentDelete(space space.Space, ct contenttype.ContentType, conte
		}
	}

	if err := t.Commit(); err != nil {
		db.log.Println("failed to delete content", err)
	}

	return nil
	return t.Commit()
}

func (db *DB) ContentPerContentType(space space.Space, ct contenttype.ContentType, page int, order OrderType, sortField string) ([]content.Content, error) {


@@ 996,24 989,47 @@ func (c *ContentValue) UnmarshalJSON(b []byte) error {

type contentIter struct {
	db        *DB
	t         *sql.Tx // Not used for the moment (we only use simple queries).
	space     space.Space
	ct        contenttype.ContentType
	sortField string
	page      int

	// For pump
	list []content.Content
	err  error
}

func (db *DB) ContentIter(space space.Space, ct contenttype.ContentType, sortField string) *contentIter {
	return &contentIter{db, space, ct, sortField, 0}
func (db *DB) ContentIter(t *sql.Tx, space space.Space, ct contenttype.ContentType, sortField string) *contentIter {
	iter := &contentIter{db, t, space, ct, sortField, 0, nil, nil}
	iter.pump()
	return iter
}

func (iter *contentIter) Next() (ret []content.Content, hasMore bool, err error) {
	ret, err = iter.db.ContentPerContentType(iter.space, iter.ct, iter.page, OrderAsc, iter.sortField)
	if err != nil {
		return
func (iter *contentIter) pump() {
	list, err := iter.db.ContentPerContentType(iter.space, iter.ct, iter.page, OrderAsc, iter.sortField)
	iter.list = list
	iter.err = err
}

func (iter *contentIter) Next() bool {
	if iter.err != nil {
		return true // Error is picked up with Scan call.
	}
	return len(iter.list) > 0
}

func (iter *contentIter) Scan() (content.Content, error) {
	var first content.Content
	if iter.err != nil {
		return first, iter.err
	}

	hasMore = len(ret) > 0
	iter.page++
	first, iter.list = iter.list[0], iter.list[1:]
	if len(iter.list) < 1 {
		iter.page++
		iter.pump()
	}

	return
	return first, nil
}

M internal/s/db/contenttype.go => internal/s/db/contenttype.go +49 -13
@@ 169,11 169,10 @@ func (db *DB) ContentTypeUpdate(space space.Space, contenttype contenttype.Conte
	return db.ContentTypeGet(space, contenttype.ID())
}

func (db *DB) ContentTypesPerSpace(space space.Space, page int) ([]contenttype.ContentType, error) {
func (db *DB) contentTypesPerSpace(t *sql.Tx, space space.Space, page int) ([]contenttype.ContentType, error) {
	var ret []contenttype.ContentType
	rows, err := db.Query(queryFindContentTypesBySpace, space.ID(), perPage, perPage*page)
	rows, err := t.Query(queryFindContentTypesBySpace, space.ID(), perPage, perPage*page)
	if err != nil {
		db.log.Println(err)
		return ret, err
	}



@@ 188,6 187,21 @@ func (db *DB) ContentTypesPerSpace(space space.Space, page int) ([]contenttype.C
	return ret, nil
}

func (db *DB) ContentTypesPerSpace(space space.Space, page int) ([]contenttype.ContentType, error) {
	t, err := db.Begin()
	if err != nil {
		return nil, err
	}
	defer t.Rollback()

	types, err := db.contentTypesPerSpace(t, space, page)
	if err != nil {
		return nil, err
	}

	return types, t.Commit()
}

func (db *DB) contentTypeGet(t *sql.Tx, space space.Space, contenttypeID string) (contenttype.ContentType, error) {
	var ct ContentType
	if err := t.QueryRow(queryFindContentTypeByIDAndSpace, contenttypeID, space.ID()).Scan(&ct.ContentTypeID, &ct.ContentTypeName); err != nil {


@@ 230,7 244,6 @@ func (db *DB) ContentTypeSearch(space space.Space, query string, page int) ([]co
	var ret []contenttype.ContentType
	rows, err := db.Query(queryFindContentTypeByNameAndSpace, fmt.Sprintf("%%%s%%", query), space.ID(), perPage, perPage*page)
	if err != nil {
		db.log.Println(err)
		return ret, err
	}



@@ 293,22 306,45 @@ func (f *ContentTypeField) Type() string {

type contentTypeIter struct {
	db    *DB
	t     *sql.Tx
	space space.Space
	page  int

	// For pump
	list []contenttype.ContentType
	err  error
}

func (db *DB) ContentTypeIter(space space.Space) *contentTypeIter {
	return &contentTypeIter{db, space, 0}
func (db *DB) ContentTypeIter(t *sql.Tx, space space.Space) *contentTypeIter {
	iter := &contentTypeIter{db, t, space, 0, nil, nil}
	iter.pump()
	return iter
}

func (iter *contentTypeIter) Next() (ret []contenttype.ContentType, hasMore bool, err error) {
	ret, err = iter.db.ContentTypesPerSpace(iter.space, iter.page)
	if err != nil {
		return
func (iter *contentTypeIter) pump() {
	list, err := iter.db.contentTypesPerSpace(iter.t, iter.space, iter.page)
	iter.list = list
	iter.err = err
}

func (iter *contentTypeIter) Next() bool {
	if iter.err != nil {
		return true // Error is picked up with Scan call.
	}
	return len(iter.list) > 0
}

	hasMore = len(ret) > 0
	iter.page++
func (iter *contentTypeIter) Scan() (contenttype.ContentType, error) {
	var first contenttype.ContentType
	if iter.err != nil {
		return first, iter.err
	}

	first, iter.list = iter.list[0], iter.list[1:]
	if len(iter.list) < 1 {
		iter.page++
		iter.pump()
	}

	return
	return first, nil
}

M internal/s/db/space.go => internal/s/db/space.go +38 -54
@@ 92,14 92,11 @@ func (db *DB) SpaceNew(user user.User, name, desc string) (space.Space, error) {
}

func (db *DB) SpaceCopy(user user.User, prevS space.Space, name, desc string) (space.Space, error) {
	// TODO: NOTE: Normal transactions don't work for us, they are too nested.
	// Figure out a workaround for this.
	// t, err := db.Begin()
	// if err != nil {
	// 	return nil, err
	// }
	// defer t.Rollback()
	t := db
	t, err := db.Begin()
	if err != nil {
		return nil, err
	}
	defer t.Rollback()

	res, err := t.Exec(queryCreateNewSpace, name, desc)
	if err != nil {


@@ 121,74 118,61 @@ func (db *DB) SpaceCopy(user user.User, prevS space.Space, name, desc string) (s
		return nil, err
	}

	next, err := db.SpaceGet(user, strconv.FormatInt(nextID, 10))
	next, err := db.spaceGet(t, user, strconv.FormatInt(nextID, 10))
	if err != nil {
		return nil, err
	}

	// Copy all content types and their value types.
	// Copy all contents and their values.
	iter := db.ContentTypeIter(prevS)
	for {
		types, hasMore, err := iter.Next()
	iter := db.ContentTypeIter(t, prevS)
	for iter.Next() {
		prevCT, err := iter.Scan()
		if err != nil {
			return nil, err
		}

		if !hasMore {
			break
		// Copy content type.
		res, err := t.Exec(copyContentTypeQuery, nextID, prevCT.ID())
		if err != nil {
			return nil, err
		}

		for _, prevCT := range types {
			// Copy content type.
			res, err := t.Exec(copyContentTypeQuery, nextID, prevCT.ID())
			if err != nil {
				return nil, err
			}
		ctID, err := res.LastInsertId()
		if err != nil {
			return nil, err
		}

		// Copy value type.
		if _, err := t.Exec(copyValueTypeQuery, ctID, prevCT.ID()); err != nil {
			return nil, err
		}

		ct, err := db.contentTypeGet(t, next, strconv.FormatInt(ctID, 10))
		if err != nil {
			return nil, err
		}

			ctID, err := res.LastInsertId()
		// Copy all contents and their values.
		iter := db.ContentIter(t, prevS, prevCT, "name") // TODO: What is this sort type for?
		for iter.Next() {
			prevC, err := iter.Scan()
			if err != nil {
				return nil, err
			}

			// Copy value type.
			if _, err := t.Exec(copyValueTypeQuery, ctID, prevCT.ID()); err != nil {
				return nil, err
			params := make([]ContentNewParam, len(prevC.Values()))
			for i, prevV := range prevC.Values() {
				params[i] = ContentNewParam{prevV.Type(), prevV.Name(), prevV.Value()}
			}

			ct, err := db.ContentTypeGet(next, strconv.FormatInt(ctID, 10))
			if err != nil {
			if _, err := db.contentNew(t, next, ct, params, defaultDepth); err != nil {
				return nil, err
			}
		}
	}

			// Copy all contents and their values.
			iter := db.ContentIter(prevS, prevCT, "name") // TODO: What is this sort type for?
			for {
				contents, hasMore, err := iter.Next()
				if err != nil {
					return nil, err
				}

				if !hasMore {
					break
				}

				for _, prevC := range contents {
					params := make([]ContentNewParam, len(prevC.Values()))
					for i, prevV := range prevC.Values() {
						params[i] = ContentNewParam{prevV.Type(), prevV.Name(), prevV.Value()}
					}

					if _, err := db.ContentNew(next, ct, params); err != nil {
						return nil, err
					}
				} // Contents
			} // ContentsIter
		} // ContentTypes
	} // ContentTypesIter

	// return next, t.Commit()
	return next, nil
	return next, t.Commit()
}

func (db *DB) spaceGet(t *sql.Tx, user user.User, spaceID string) (space.Space, error) {