diff --git a/design/memoize.md b/design/memoize.md
new file mode 100644
index 00000000..27f0569f
--- /dev/null
+++ b/design/memoize.md
@@ -0,0 +1,90 @@
+# Memoizing hydrate funcs
+
+When a HydrateFunc is memoized, the result of the hydrate func is cached in memory for the duration of the plugin run. This is useful for hydrate funcs which are expensive to run and are called multiple times during a single plugin run.
+
+When memoizing a func, a new function is returned which wraps the underlying func in cache get/set logic. The original func is not modified.
+
+A map of original function names is maintained, keyed by the pointer to the wrapped function. THis allows retrieval of the underlying function name from the wrapped function. 76 
+
+*** This does not work - all anon function have the same pointer so the map wil only have a single entry *** 
+
+
+Who uses memoized name map/NamedHydrateFunc
+
+## IsMemoized 
+used for rate limiters - still works as all memoized functions will appear in the map 
+
+## newNamedHydrateFunc
+Who creates named hydrate call? This is crucial as we MUST NOT call newNamedHydrateFunc with a memoized hydrate func
+
+### hydrate call depends
+```
+func newHydrateCall(config *HydrateConfig, d *QueryData) (*hydrateCall, error) {
+	res := &hydrateCall{
+		Config:    config,
+		queryData: d,
+		// default to empty limiter
+		rateLimiter: rate_limiter.EmptyMultiLimiter(),
+	}
+	res.NamedHydrateFunc = *config.NamedHydrate
+
+	for _, f := range config.Depends {
+		res.Depends = append(res.Depends, newNamedHydrateFunc(f))
+	}
+
+	return res, nil
+}
+```
+
+### RetryHydrate
+Doesn't seem to be used anywhere
+```
+func RetryHydrate(ctx context.Context, d *QueryData, hydrateData *HydrateData, hydrate HydrateFunc, retryConfig *RetryConfig) (hydrateResult interface{}, err error) {
+	return retryNamedHydrate(ctx, d, hydrateData, newNamedHydrateFunc(hydrate), retryConfig)
+}
+```
+
+## Name
+
+Who references the name field
+
+### HydrateConfig tags
+GetConfig/ListConfig/HydrateConfig has a map ot tags which is auto populated with the hydrate name
+
+
+### hydrateCall canStart
+```
+
+// check whether all hydrate functions we depend on have saved their results
+for _, dep := range h.Depends {
+    if !helpers.StringSliceContains(rowData.getHydrateKeys(), dep.Name) {
+        return false
+    }
+}
+```
+
+### hydrateCall start
+
+```
+// retrieve the concurrencyDelay for the call
+concurrencyDelay := r.getHydrateConcurrencyDelay(h.Name)
+```
+
+### ListConfig Validate
+```
+// ensure that if there is an explicit hydrate config for the list hydrate, it does not declare dependencies
+listHydrateName := table.List.namedHydrate.Name
+for _, h := range table.HydrateConfig {
+    if h.namedHydrate.Name == listHydrateName {
+    ...
+}
+```
+
+### Plugin buildHydrateConfigMap
+```
+for i := range p.HydrateConfig {
+		h := &p.HydrateConfig[i]
+		h.initialise(nil)
+		funcName := h.namedHydrate.Name
+		p.hydrateConfigMap[funcName] = h
+```
diff --git a/go.mod b/go.mod
index 9147fd1a..2cf8c467 100644
--- a/go.mod
+++ b/go.mod
@@ -18,7 +18,7 @@ require (
 	github.com/olekukonko/tablewriter v0.0.5
 	github.com/sethvargo/go-retry v0.2.4
 	github.com/stevenle/topsort v0.2.0
-	github.com/turbot/go-kit v0.9.0-rc.1
+	github.com/turbot/go-kit v0.9.0
 	github.com/zclconf/go-cty v1.14.1
 	go.opentelemetry.io/otel v1.21.0
 	go.opentelemetry.io/otel/metric v1.21.0 // indirect
diff --git a/go.sum b/go.sum
index 217f7327..f7ec73bd 100644
--- a/go.sum
+++ b/go.sum
@@ -556,8 +556,8 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU
 github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
 github.com/tkrajina/go-reflector v0.5.6 h1:hKQ0gyocG7vgMD2M3dRlYN6WBBOmdoOzJ6njQSepKdE=
 github.com/tkrajina/go-reflector v0.5.6/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4=
-github.com/turbot/go-kit v0.9.0-rc.1 h1:6j1IidB4LpTw0TDXY0DSY6UxtrjMr0KIOBk3glO3Xfk=
-github.com/turbot/go-kit v0.9.0-rc.1/go.mod h1:BrOy6Xeizj+eBzXOOWuBMSzQosirN+IGw9MksKULvd4=
+github.com/turbot/go-kit v0.9.0 h1:7RVIFpHa0vdsh8GMEr4cM+D4jQ7h4pGeFmT2EVG/U5Y=
+github.com/turbot/go-kit v0.9.0/go.mod h1:fFQqR59I5z5JeeBLfK1PjSifn4Oprs3NiQx0CxeSJxs=
 github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=
 github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
 github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
diff --git a/plugin/column.go b/plugin/column.go
index f9bae9bc..9cacc6b6 100644
--- a/plugin/column.go
+++ b/plugin/column.go
@@ -75,24 +75,34 @@ type Column struct {
 	Description string
 	// explicitly specify the function which populates this data
 	// - this is only needed if any of the default hydrate functions will NOT return this column
-	Hydrate HydrateFunc
+	Hydrate      HydrateFunc
+	NamedHydrate NamedHydrateFunc
 	// the default column value
 	Default interface{}
 	//  a list of transforms to generate the column value
 	Transform *transform.ColumnTransforms
 }
 
-// QueryColumn is struct storing column name and resolved hydrate name
-// this is used in the query data when the hydrate function has been resolved
-type QueryColumn struct {
-	*Column
-	// the name of the hydrate function which will be used to populate this column
-	// - this may be a default hydrate function
-	hydrateName string
+func (c *Column) initialise() {
+	if c.Hydrate == nil && c.NamedHydrate.empty() {
+		return
+	}
+	// populate the named hydrate funcs
+	if c.NamedHydrate.empty() {
+		// create a named hydrate func, assuming this function is not memoized
+		c.NamedHydrate = newNamedHydrateFunc(c.Hydrate)
+	} else {
+		// a named hydrate was explicitly specified - probably meaning the hydrate is memoized
+		// call initialize to populate IsMemoized
+		c.NamedHydrate.initialize()
+		// be sure to also set the Hydrate property to the underlying func
+		c.Hydrate = c.NamedHydrate.Func
+	}
+
 }
 
 // ToColumnValue converts a value of unknown type to a valid protobuf column value.type
-func (c Column) ToColumnValue(val any) (*proto.Column, error) {
+func (c *Column) ToColumnValue(val any) (*proto.Column, error) {
 	defer func() {
 		if r := recover(); r != nil {
 			panic(fmt.Errorf("%s: %v", c.Name, r))
@@ -213,7 +223,15 @@ func (c Column) ToColumnValue(val any) (*proto.Column, error) {
 	}
 
 	return columnValue, nil
+}
 
+// QueryColumn is struct storing column name and resolved hydrate name (including List/Get call)
+// this is used in the query data when the hydrate function has been resolved
+type QueryColumn struct {
+	*Column
+	// the name of the hydrate function which will be used to populate this column
+	// - this may be a default hydrate function
+	hydrateName string
 }
 
 func NewQueryColumn(column *Column, hydrateName string) *QueryColumn {
diff --git a/plugin/get_config.go b/plugin/get_config.go
index 32e18664..68bb5316 100644
--- a/plugin/get_config.go
+++ b/plugin/get_config.go
@@ -76,7 +76,7 @@ type GetConfig struct {
 	// Deprecated: use IgnoreConfig
 	ShouldIgnoreError ErrorPredicate
 	MaxConcurrency    int
-	namedHydrate      namedHydrateFunc
+	NamedHydrate      NamedHydrateFunc
 }
 
 // initialise the GetConfig
@@ -106,7 +106,7 @@ func (c *GetConfig) initialise(table *Table) {
 		c.Tags = make(map[string]string)
 	}
 	// add in function name to tags
-	c.Tags[rate_limiter.RateLimiterScopeFunction] = c.namedHydrate.Name
+	c.Tags[rate_limiter.RateLimiterScopeFunction] = c.NamedHydrate.Name
 
 	// copy the (deprecated) top level ShouldIgnoreError property into the ignore config
 	if c.IgnoreConfig.ShouldIgnoreError == nil {
@@ -128,7 +128,16 @@ func (c *GetConfig) initialise(table *Table) {
 	log.Printf("[TRACE] GetConfig.initialise complete: RetryConfig: %s, IgnoreConfig: %s", c.RetryConfig.String(), c.IgnoreConfig.String())
 
 	// populate the named hydrate func
-	c.namedHydrate = newNamedHydrateFunc(c.Hydrate)
+	if c.NamedHydrate.empty() {
+		// create a named hydrate func, assuming this function is not memoized
+		c.NamedHydrate = newNamedHydrateFunc(c.Hydrate)
+	} else {
+		// a named hydrate was explicitly specified - probably meaning the hydrate is memoized
+		// call initialize to populate IsMemoized
+		c.NamedHydrate.initialize()
+		// be sure to also set the Hydrate property to the underlying func
+		c.Hydrate = c.NamedHydrate.Func
+	}
 
 }
 
diff --git a/plugin/hydrate_cache.go b/plugin/hydrate_cache.go
index b5f86bdb..a5296e79 100644
--- a/plugin/hydrate_cache.go
+++ b/plugin/hydrate_cache.go
@@ -4,22 +4,12 @@ import (
 	"context"
 	"fmt"
 	"log"
-	"reflect"
 	"sync"
 	"time"
 
 	"github.com/turbot/go-kit/helpers"
 )
 
-// map of memoized functions to the original function name
-var memoizedNameMap = make(map[uintptr]string)
-var memoizedNameMapLock sync.RWMutex
-
-// map of currently executing memoized hydrate funcs
-
-var memoizedHydrateFunctionsPending = make(map[string]*sync.WaitGroup)
-var memoizedHydrateLock sync.RWMutex
-
 /*
 HydrateFunc is a function that gathers data to build table rows.
 Typically this would make an API call and return the raw API output.
@@ -66,6 +56,11 @@ Use it to reduce the number of API calls if the HydrateFunc is used by multiple
 	}
 */
 func (f HydrateFunc) Memoize(opts ...MemoizeOption) HydrateFunc {
+	// TODO determine if this is already memoized
+	// if so, return the existing memoized function
+
+	log.Printf("[INFO] Memoize %p %s", f, helpers.GetFunctionName(f))
+
 	config := newMemoizeConfiguration(f)
 	for _, o := range opts {
 		o(config)
@@ -130,11 +125,9 @@ func (f HydrateFunc) Memoize(opts ...MemoizeOption) HydrateFunc {
 		log.Printf("[TRACE] Memoize (connection %s, cache key %s) - no pending call found so calling and caching hydrate", d.Connection.Name, cacheKey)
 		// no call the hydrate function and cache the result
 		return callAndCacheHydrate(ctx, d, h, f, cacheKey, ttl)
-
 	}
 
-	// store the memoized func in the name map
-	f.setMemoizedFuncName(memoizedFunc)
+	log.Printf("[INFO] Memoize %p %s", f, helpers.GetFunctionName(f))
 
 	return memoizedFunc
 }
@@ -189,27 +182,3 @@ func callAndCacheHydrate(ctx context.Context, d *QueryData, h *HydrateData, hydr
 	// return the hydrate data
 	return hydrateData, nil
 }
-
-// return the function name
-// if this function has been memoized, return the underlying function name
-func (f HydrateFunc) getOriginalFuncName() (name string, isMemoized bool) {
-	memoizedNameMapLock.RLock()
-	// check if this is a memoized function, if so get the original name
-	p := reflect.ValueOf(f).Pointer()
-	name, isMemoized = memoizedNameMap[p]
-	memoizedNameMapLock.RUnlock()
-
-	if !isMemoized {
-		name = helpers.GetFunctionName(f)
-	}
-
-	return name, isMemoized
-}
-
-func (f HydrateFunc) setMemoizedFuncName(memoizedFunc HydrateFunc) {
-	// add to map
-	memoizedNameMapLock.Lock()
-	p := reflect.ValueOf(memoizedFunc).Pointer()
-	memoizedNameMap[p] = helpers.GetFunctionName(f)
-	memoizedNameMapLock.Unlock()
-}
diff --git a/plugin/hydrate_call.go b/plugin/hydrate_call.go
index ca20a23b..9d53b577 100644
--- a/plugin/hydrate_call.go
+++ b/plugin/hydrate_call.go
@@ -11,9 +11,9 @@ import (
 
 // hydrateCall struct encapsulates a hydrate call, its config and dependencies
 type hydrateCall struct {
-	namedHydrateFunc
+	NamedHydrateFunc
 	// the dependencies expressed using function name
-	Depends []namedHydrateFunc
+	Depends []NamedHydrateFunc
 	Config  *HydrateConfig
 
 	queryData   *QueryData
@@ -27,7 +27,7 @@ func newHydrateCall(config *HydrateConfig, d *QueryData) (*hydrateCall, error) {
 		// default to empty limiter
 		rateLimiter: rate_limiter.EmptyMultiLimiter(),
 	}
-	res.namedHydrateFunc = newNamedHydrateFunc(config.Func)
+	res.NamedHydrateFunc = config.namedHydrate
 
 	for _, f := range config.Depends {
 		res.Depends = append(res.Depends, newNamedHydrateFunc(f))
@@ -38,7 +38,7 @@ func newHydrateCall(config *HydrateConfig, d *QueryData) (*hydrateCall, error) {
 
 func (h *hydrateCall) shallowCopy() *hydrateCall {
 	return &hydrateCall{
-		namedHydrateFunc: namedHydrateFunc{
+		NamedHydrateFunc: NamedHydrateFunc{
 			Func: h.Func,
 			Name: h.Name,
 		},
@@ -111,7 +111,7 @@ func (h *hydrateCall) start(ctx context.Context, r *rowData, d *QueryData) time.
 
 	// call callHydrate async, ignoring return values
 	go func() {
-		r.callHydrate(ctx, d, h.namedHydrateFunc, h.Config)
+		r.callHydrate(ctx, d, h.NamedHydrateFunc, h.Config)
 		h.onFinished()
 	}()
 	// retrieve the concurrencyDelay for the call
diff --git a/plugin/hydrate_config.go b/plugin/hydrate_config.go
index 1fb76202..6854fb08 100644
--- a/plugin/hydrate_config.go
+++ b/plugin/hydrate_config.go
@@ -114,7 +114,7 @@ type HydrateConfig struct {
 	// Deprecated: use IgnoreConfig
 	ShouldIgnoreError ErrorPredicate
 
-	namedHydrate namedHydrateFunc
+	namedHydrate NamedHydrateFunc
 }
 
 func (c *HydrateConfig) String() string {
@@ -137,6 +137,7 @@ ScopeValues: %s`,
 }
 
 func (c *HydrateConfig) initialise(table *Table) {
+	// create a named hydrate func
 	c.namedHydrate = newNamedHydrateFunc(c.Func)
 
 	log.Printf("[TRACE] HydrateConfig.initialise func %s, table %s", c.namedHydrate.Name, table.Name)
diff --git a/plugin/hydrate_error.go b/plugin/hydrate_error.go
index e64ec8c0..c4ff4f5d 100644
--- a/plugin/hydrate_error.go
+++ b/plugin/hydrate_error.go
@@ -18,7 +18,7 @@ func RetryHydrate(ctx context.Context, d *QueryData, hydrateData *HydrateData, h
 	return retryNamedHydrate(ctx, d, hydrateData, newNamedHydrateFunc(hydrate), retryConfig)
 }
 
-func retryNamedHydrate(ctx context.Context, d *QueryData, hydrateData *HydrateData, hydrate namedHydrateFunc, retryConfig *RetryConfig) (hydrateResult interface{}, err error) {
+func retryNamedHydrate(ctx context.Context, d *QueryData, hydrateData *HydrateData, hydrate NamedHydrateFunc, retryConfig *RetryConfig) (hydrateResult interface{}, err error) {
 	ctx, span := telemetry.StartSpan(ctx, d.Table.Plugin.Name, "RetryHydrate (%s)", d.Table.Name)
 	span.SetAttributes(
 		attribute.String("hydrate-func", hydrate.Name),
@@ -108,7 +108,7 @@ func getBackoff(retryConfig *RetryConfig) (retry.Backoff, error) {
 }
 
 // WrapHydrate is a higher order function which returns a [HydrateFunc] that handles Ignorable errors.
-func WrapHydrate(hydrate namedHydrateFunc, ignoreConfig *IgnoreConfig) namedHydrateFunc {
+func WrapHydrate(hydrate NamedHydrateFunc, ignoreConfig *IgnoreConfig) NamedHydrateFunc {
 	res := hydrate.clone()
 
 	res.Func = func(ctx context.Context, d *QueryData, h *HydrateData) (item interface{}, err error) {
diff --git a/plugin/list_config.go b/plugin/list_config.go
index 9e1f1b3b..95ef58cf 100644
--- a/plugin/list_config.go
+++ b/plugin/list_config.go
@@ -50,9 +50,10 @@ type ListConfig struct {
 	ParentTags map[string]string
 
 	// Deprecated: Use IgnoreConfig
-	ShouldIgnoreError  ErrorPredicate
-	namedHydrate       *namedHydrateFunc
-	namedParentHydrate *namedHydrateFunc
+	ShouldIgnoreError ErrorPredicate
+
+	NamedHydrate       NamedHydrateFunc
+	NamedParentHydrate NamedHydrateFunc
 }
 
 func (c *ListConfig) initialise(table *Table) {
@@ -86,16 +87,31 @@ func (c *ListConfig) initialise(table *Table) {
 	c.IgnoreConfig.DefaultTo(table.DefaultIgnoreConfig)
 
 	// populate the named hydrate funcs
-	n := newNamedHydrateFunc(c.Hydrate)
-	c.namedHydrate = &n
+	if c.NamedHydrate.empty() {
+		c.NamedHydrate = newNamedHydrateFunc(c.Hydrate)
+	} else {
+		// a named hydrate was explicitly specified - probably meaning the hydrate is memoized
+		// call initialize to populate IsMemoized
+		c.NamedHydrate.initialize()
+		// be sure to also set the Hydrate property to the underlying func
+		c.Hydrate = c.NamedHydrate.Func
+	}
 	// add in function name to tags
-	c.Tags[rate_limiter.RateLimiterScopeFunction] = c.namedHydrate.Name
+	c.Tags[rate_limiter.RateLimiterScopeFunction] = c.NamedHydrate.Name
 
 	if c.ParentHydrate != nil {
-		p := newNamedHydrateFunc(c.ParentHydrate)
-		c.namedParentHydrate = &p
+		if c.NamedParentHydrate.empty() {
+			c.NamedParentHydrate = newNamedHydrateFunc(c.ParentHydrate)
+		} else {
+			// a named hydrate was explicitly specified - probably meaning the hydrate is memoized
+			// call initialize to populate IsMemoized
+			c.NamedParentHydrate.initialize()
+			// be sure to also set the Hydrate property to the underlying func
+			c.ParentHydrate = c.NamedParentHydrate.Func
+		}
+
 		// add in parent function name to tags
-		c.ParentTags[rate_limiter.RateLimiterScopeFunction] = c.namedParentHydrate.Name
+		c.ParentTags[rate_limiter.RateLimiterScopeFunction] = c.NamedParentHydrate.Name
 	}
 
 	log.Printf("[TRACE] ListConfig.initialise complete: RetryConfig: %s, IgnoreConfig %s", c.RetryConfig.String(), c.IgnoreConfig.String())
@@ -114,7 +130,7 @@ func (c *ListConfig) Validate(table *Table) []string {
 	}
 
 	// ensure that if there is an explicit hydrate config for the list hydrate, it does not declare dependencies
-	listHydrateName := table.List.namedHydrate.Name
+	listHydrateName := table.List.NamedHydrate.Name
 	for _, h := range table.HydrateConfig {
 		if h.namedHydrate.Name == listHydrateName {
 			if len(h.Depends) > 0 {
diff --git a/plugin/memoize.go b/plugin/memoize.go
new file mode 100644
index 00000000..54b76fe0
--- /dev/null
+++ b/plugin/memoize.go
@@ -0,0 +1,35 @@
+package plugin
+
+import (
+	"github.com/turbot/go-kit/helpers"
+	"sync"
+)
+
+// map of currently executing memoized hydrate funcs
+
+var memoizedHydrateFunctionsPending = make(map[string]*sync.WaitGroup)
+var memoizedHydrateLock sync.RWMutex
+
+/*
+		MemoizeHydrate creates a memoized version of the supplied hydrate function and returns a NamedHydrateFunc
+	    populated with the original function name.
+
+# Usage
+
+	{
+		Name:        "account",
+		Type:        proto.ColumnType_STRING,
+		NamedHydrate:  plugin.Memoize(getCommonColumns)),
+		Description: "The Snowflake account ID.",
+		Transform:   transform.FromCamel(),
+	}
+*/
+func MemoizeHydrate(hydrateFunc HydrateFunc, opts ...MemoizeOption) NamedHydrateFunc {
+	memoized := hydrateFunc.Memoize(opts...)
+
+	return NamedHydrateFunc{
+		Func: memoized,
+		// store the original function name
+		Name: helpers.GetFunctionName(hydrateFunc),
+	}
+}
diff --git a/plugin/named_hydrate_func.go b/plugin/named_hydrate_func.go
index db02380d..390a128d 100644
--- a/plugin/named_hydrate_func.go
+++ b/plugin/named_hydrate_func.go
@@ -1,22 +1,34 @@
 package plugin
 
-type namedHydrateFunc struct {
+import "github.com/turbot/go-kit/helpers"
+
+type NamedHydrateFunc struct {
 	Func       HydrateFunc
 	Name       string
 	IsMemoized bool
 }
 
-func newNamedHydrateFunc(f HydrateFunc) namedHydrateFunc {
-	res := namedHydrateFunc{
+func newNamedHydrateFunc(f HydrateFunc) NamedHydrateFunc {
+	res := NamedHydrateFunc{
 		Func: f,
+		Name: helpers.GetFunctionName(f),
 	}
-	res.Name, res.IsMemoized = f.getOriginalFuncName()
+
 	return res
 }
 
-func (h namedHydrateFunc) clone() namedHydrateFunc {
-	return namedHydrateFunc{
+func (h NamedHydrateFunc) clone() NamedHydrateFunc {
+	return NamedHydrateFunc{
 		Func: h.Func,
 		Name: h.Name,
 	}
 }
+
+// determine whether we are memoized
+func (h NamedHydrateFunc) initialize() {
+	h.IsMemoized = h.Name == helpers.GetFunctionName(h.Func)
+}
+
+func (h NamedHydrateFunc) empty() bool {
+	return h.Func == nil
+}
diff --git a/plugin/query_data.go b/plugin/query_data.go
index c817d1fc..674b265c 100644
--- a/plugin/query_data.go
+++ b/plugin/query_data.go
@@ -152,8 +152,8 @@ type QueryData struct {
 
 	fetchMetadata         *hydrateMetadata
 	parentHydrateMetadata *hydrateMetadata
-	listHydrate           *namedHydrateFunc
-	childHydrate          *namedHydrateFunc
+	listHydrate           NamedHydrateFunc
+	childHydrate          NamedHydrateFunc
 }
 
 func newQueryData(connectionCallId string, p *Plugin, queryContext *QueryContext, table *Table, connectionData *ConnectionData, executeData *proto.ExecuteConnectionData, outputChan chan *proto.ExecuteResponse) (*QueryData, error) {
@@ -394,7 +394,7 @@ func (d *QueryData) populateMatrixPropertyNames() {
 // first. BEFORE the other hydration functions
 // NOTE2: this function also populates the resolvedHydrateName for each column (used to retrieve column values),
 // and the hydrateColumnMap (used to determine which columns to return)
-func (d *QueryData) populateRequiredHydrateCalls() {
+func (d *QueryData) populateRequiredHydrateCalls() error {
 	t := d.Table
 	colsUsed := d.QueryContext.Columns
 	fetchType := d.FetchType
@@ -424,12 +424,13 @@ func (d *QueryData) populateRequiredHydrateCalls() {
 			hydrateName = fetchFunc.Name
 		} else {
 			// there is a hydrate call registered
-			namedFunc := newNamedHydrateFunc(hydrateFunc)
-			hydrateName = namedFunc.Name
+			hydrateName = column.NamedHydrate.Name
 
 			// if this column was requested in query, add the hydrate call to required calls
 			if helpers.StringSliceContains(colsUsed, column.Name) {
-				requiredCallBuilder.Add(namedFunc, d.connectionCallId)
+				if err := requiredCallBuilder.Add(column.NamedHydrate, d.connectionCallId); err != nil {
+					return err
+				}
 			}
 		}
 
@@ -440,6 +441,7 @@ func (d *QueryData) populateRequiredHydrateCalls() {
 
 	// now we have all the hydrate calls, build a list of all the columns that will be returned by the hydrate functions.
 	// these will be used for the cache
+	return nil
 }
 
 // build list of all columns returned by the fetch call and required hydrate calls
@@ -457,7 +459,6 @@ func (d *QueryData) populateColumns() {
 // get the column returned by the given hydrate call
 func (d *QueryData) addColumnsForHydrate(hydrateName string) {
 	for _, columnName := range d.hydrateColumnMap[hydrateName] {
-
 		// get the column from the table
 		column := d.Table.getColumn(columnName)
 		d.columns[columnName] = NewQueryColumn(column, hydrateName)
@@ -471,7 +472,7 @@ func (d *QueryData) setMatrixItem(matrixItem map[string]interface{}) {
 	log.Printf("[INFO] setMatrixItem %s", matrixItem)
 	for col, value := range matrixItem {
 		qualValue := proto.NewQualValue(value)
-		// replace any existing entry for both Quals and EqualsQuals
+
 		d.EqualsQuals[col] = qualValue
 		d.Quals[col] = &KeyColumnQuals{Name: col, Quals: []*quals.Qual{{Column: col, Operator: quals.QualOperatorEqual, Value: qualValue}}}
 	}
@@ -545,13 +546,13 @@ func (d *QueryData) verifyCallerIsListCall(callingFunction string) bool {
 	if d.Table.List == nil {
 		return false
 	}
-	listFunction := d.Table.List.namedHydrate.Name
-	listParentFunction := d.Table.List.namedParentHydrate.Name
+	listFunction := d.Table.List.NamedHydrate.Name
+	listParentFunction := d.Table.List.NamedParentHydrate.Name
 	if callingFunction != listFunction && callingFunction != listParentFunction {
 		// if the calling function is NOT one of the other registered hydrate functions,
 		//it must be an anonymous function so let it go
 		for _, c := range d.Table.Columns {
-			if c.Hydrate != nil && newNamedHydrateFunc(c.Hydrate).Name == callingFunction {
+			if c.NamedHydrate.Name == callingFunction {
 				return false
 			}
 		}
@@ -659,7 +660,7 @@ func (d *QueryData) streamLeafListItem(ctx context.Context, items ...interface{}
 		// set the parent item on the row data
 		rd.parentItem = d.parentItem
 		// NOTE: add the item as the hydrate data for the list call
-		rd.set(d.Table.List.namedHydrate.Name, item)
+		rd.set(d.Table.List.NamedHydrate.Name, item)
 
 		d.rowDataChan <- rd
 	}
@@ -702,11 +703,10 @@ func (d *QueryData) buildRowsAsync(ctx context.Context, rowChan chan *proto.Row,
 				logging.LogTime("got rowData - calling getRow")
 				// is there any more data?
 				if rowData == nil {
-					log.Printf("[INFO] rowData chan returned nil - wait for rows to complete (%s)", d.connectionCallId)
+					log.Printf("[TRACE] rowData chan returned nil - wait for rows to complete (%s)", d.connectionCallId)
 					// now we know there will be no more items, close row chan when the wait group is complete
 					// this allows time for all hydrate goroutines to complete
 					d.waitForRowsToComplete(&rowWg, rowChan)
-					log.Printf("[INFO] buildRowsAsync goroutine returning (%s)", d.connectionCallId)
 					// rowData channel closed - nothing more to do
 					return
 				}
@@ -916,7 +916,7 @@ func (d *QueryData) removeReservedColumns(row *proto.Row) {
 	}
 }
 
-func (d *QueryData) setListCalls(listCall, childHydrate *namedHydrateFunc) {
+func (d *QueryData) setListCalls(listCall, childHydrate NamedHydrateFunc) {
 	d.listHydrate = listCall
 	d.childHydrate = childHydrate
 }
diff --git a/plugin/query_data_rate_limiters.go b/plugin/query_data_rate_limiters.go
index baf9ca33..95528128 100644
--- a/plugin/query_data_rate_limiters.go
+++ b/plugin/query_data_rate_limiters.go
@@ -96,7 +96,7 @@ func (d *QueryData) resolveGetRateLimiters() error {
 	// NOTE: RateLimit cannot be nil as it is initialized to an empty struct if needed
 	getLimiter, err := d.plugin.getHydrateCallRateLimiter(d.Table.Get.Tags, d)
 	if err != nil {
-		log.Printf("[WARN] get call %s getHydrateCallRateLimiter failed: %s (%s)", d.Table.Get.namedHydrate.Name, err.Error(), d.connectionCallId)
+		log.Printf("[WARN] get call %s getHydrateCallRateLimiter failed: %s (%s)", d.Table.Get.NamedHydrate.Name, err.Error(), d.connectionCallId)
 		return err
 	}
 
@@ -111,7 +111,7 @@ func (d *QueryData) resolveParentChildRateLimiters() error {
 	// resolve the parent hydrate rate limiter
 	parentRateLimiter, err := d.plugin.getHydrateCallRateLimiter(d.Table.List.ParentTags, d)
 	if err != nil {
-		log.Printf("[WARN] resolveParentChildRateLimiters: %s: getHydrateCallRateLimiter failed: %s (%s)", d.Table.List.namedParentHydrate.Name, err.Error(), d.connectionCallId)
+		log.Printf("[WARN] resolveParentChildRateLimiters: %s: getHydrateCallRateLimiter failed: %s (%s)", d.Table.List.NamedParentHydrate.Name, err.Error(), d.connectionCallId)
 		return err
 	}
 	// assign the parent rate limiter to d.fetchLimiters
@@ -120,7 +120,7 @@ func (d *QueryData) resolveParentChildRateLimiters() error {
 	// resolve the child  hydrate rate limiter
 	childRateLimiter, err := d.plugin.getHydrateCallRateLimiter(d.Table.List.Tags, d)
 	if err != nil {
-		log.Printf("[WARN] resolveParentChildRateLimiters: %s: getHydrateCallRateLimiter failed: %s (%s)", d.Table.List.namedHydrate.Name, err.Error(), d.connectionCallId)
+		log.Printf("[WARN] resolveParentChildRateLimiters: %s: getHydrateCallRateLimiter failed: %s (%s)", d.Table.List.NamedHydrate.Name, err.Error(), d.connectionCallId)
 		return err
 	}
 	d.fetchLimiters.childListRateLimiter = childRateLimiter
@@ -132,7 +132,7 @@ func (d *QueryData) resolveListRateLimiters() error {
 	// NOTE: RateLimit cannot be nil as it is initialized to an empty struct if needed
 	listLimiter, err := d.plugin.getHydrateCallRateLimiter(d.Table.List.Tags, d)
 	if err != nil {
-		log.Printf("[WARN] get call %s getHydrateCallRateLimiter failed: %s (%s)", d.Table.Get.namedHydrate.Name, err.Error(), d.connectionCallId)
+		log.Printf("[WARN] get call %s getHydrateCallRateLimiter failed: %s (%s)", d.Table.Get.NamedHydrate.Name, err.Error(), d.connectionCallId)
 		return err
 	}
 	d.fetchLimiters.rateLimiter = listLimiter
@@ -146,7 +146,7 @@ func (d *QueryData) setListMetadata(fetchDelay time.Duration) {
 		ScopeValues:  d.fetchLimiters.rateLimiter.ScopeValues,
 		DelayMs:      fetchDelay.Milliseconds(),
 	}
-	if d.childHydrate == nil {
+	if d.childHydrate.empty() {
 		fetchMetadata.Type = string(fetchTypeList)
 		d.fetchMetadata = fetchMetadata
 	} else {
@@ -164,7 +164,7 @@ func (d *QueryData) setListMetadata(fetchDelay time.Duration) {
 func (d *QueryData) setGetLimiterMetadata(fetchDelay time.Duration) {
 	d.fetchMetadata = &hydrateMetadata{
 		Type:         string(fetchTypeGet),
-		FuncName:     d.Table.Get.namedHydrate.Name,
+		FuncName:     d.Table.Get.NamedHydrate.Name,
 		RateLimiters: d.fetchLimiters.rateLimiter.LimiterNames(),
 		ScopeValues:  d.fetchLimiters.rateLimiter.ScopeValues,
 		DelayMs:      fetchDelay.Milliseconds(),
diff --git a/plugin/required_hydrate_calls.go b/plugin/required_hydrate_calls.go
index 0c6374a5..0d55ae8f 100644
--- a/plugin/required_hydrate_calls.go
+++ b/plugin/required_hydrate_calls.go
@@ -1,6 +1,7 @@
 package plugin
 
 import (
+	"fmt"
 	"log"
 )
 
@@ -19,7 +20,7 @@ func newRequiredHydrateCallBuilder(d *QueryData, fetchCallName string) *required
 	}
 }
 
-func (c requiredHydrateCallBuilder) Add(hydrateFunc namedHydrateFunc, callId string) error {
+func (c requiredHydrateCallBuilder) Add(hydrateFunc NamedHydrateFunc, callId string) error {
 	hydrateName := hydrateFunc.Name
 
 	// if the resolved hydrate call is NOT the same as the fetch call, add to the map of hydrate functions to call
@@ -30,7 +31,10 @@ func (c requiredHydrateCallBuilder) Add(hydrateFunc namedHydrateFunc, callId str
 
 		// get the config for this hydrate function
 		config := getHydrateConfig(c, hydrateName)
-
+		if config == nil {
+			log.Printf("[WARN] failed to find hydrate config for %s", hydrateName)
+			return fmt.Errorf("failed to find hydrate config for %s", hydrateName)
+		}
 		call, err := newHydrateCall(config, c.queryData)
 		if err != nil {
 			log.Printf("[WARN] failed to add a hydrate call for %s: %s", hydrateName, err.Error())
diff --git a/plugin/row_data.go b/plugin/row_data.go
index f6fb88f4..74015bda 100644
--- a/plugin/row_data.go
+++ b/plugin/row_data.go
@@ -167,7 +167,7 @@ func (r *rowData) getColumnValues(ctx context.Context) (*proto.Row, error) {
 }
 
 // invoke a hydrate function, and set results on the rowData object. Stream errors on the rowData error channel
-func (r *rowData) callHydrate(ctx context.Context, d *QueryData, hydrate namedHydrateFunc, hydrateConfig *HydrateConfig) {
+func (r *rowData) callHydrate(ctx context.Context, d *QueryData, hydrate NamedHydrateFunc, hydrateConfig *HydrateConfig) {
 	// handle panics in the row hydrate function
 	defer func() {
 		if p := recover(); p != nil {
@@ -195,7 +195,7 @@ func (r *rowData) callHydrate(ctx context.Context, d *QueryData, hydrate namedHy
 }
 
 // invoke a hydrate function, retrying as required based on the retry config, and return the result and/or error
-func (r *rowData) callHydrateWithRetries(ctx context.Context, d *QueryData, hydrate namedHydrateFunc, ignoreConfig *IgnoreConfig, retryConfig *RetryConfig) (hydrateResult interface{}, err error) {
+func (r *rowData) callHydrateWithRetries(ctx context.Context, d *QueryData, hydrate NamedHydrateFunc, ignoreConfig *IgnoreConfig, retryConfig *RetryConfig) (hydrateResult interface{}, err error) {
 	ctx, span := telemetry.StartSpan(ctx, r.table.Plugin.Name, "rowData.callHydrateWithRetries (%s)", r.table.Name)
 
 	span.SetAttributes(
diff --git a/plugin/table.go b/plugin/table.go
index 70cac6e9..055009f0 100644
--- a/plugin/table.go
+++ b/plugin/table.go
@@ -126,6 +126,10 @@ func (t *Table) initialise(p *Plugin) {
 		log.Printf("[TRACE] t.List.initialise")
 		t.List.initialise(t)
 	}
+	// initialise columns
+	for _, c := range t.Columns {
+		c.initialise()
+	}
 
 	// HydrateConfig contains explicit config for hydrate functions but there may be other hydrate functions
 	// declared for specific columns which do not have config defined
@@ -174,7 +178,7 @@ func (t *Table) buildHydrateConfigMap() {
 	// NOTE: the get config may be used as a column hydrate function so add this into the map
 	if get := t.Get; get != nil {
 		// create and initialise a new hydrate config for the get func
-		t.hydrateConfigMap[get.namedHydrate.Name] = t.hydrateConfigFromGet(t.Get)
+		t.hydrateConfigMap[get.NamedHydrate.Name] = t.hydrateConfigFromGet(t.Get)
 	}
 
 	// now add all hydrate functions with no explicit config
@@ -182,9 +186,8 @@ func (t *Table) buildHydrateConfigMap() {
 		if c.Hydrate == nil {
 			continue
 		}
-		// to get name, create a namedHydrate - this will take care of mapping memoized function namedHydrate
-		hydrateName := newNamedHydrateFunc(c.Hydrate).Name
-
+		// get name
+		hydrateName := c.NamedHydrate.Name
 		if _, ok := t.hydrateConfigMap[hydrateName]; !ok {
 			t.hydrateConfigMap[hydrateName] = t.newHydrateConfig(c.Hydrate)
 		}
@@ -215,9 +218,9 @@ func (t *Table) hydrateConfigFromGet(get *GetConfig) *HydrateConfig {
 	return c
 }
 
-func (t *Table) getFetchFunc(fetchType fetchType) namedHydrateFunc {
+func (t *Table) getFetchFunc(fetchType fetchType) NamedHydrateFunc {
 	if fetchType == fetchTypeList {
-		return *t.List.namedHydrate
+		return t.List.NamedHydrate
 	}
-	return t.Get.namedHydrate
+	return t.Get.NamedHydrate
 }
diff --git a/plugin/table_fetch.go b/plugin/table_fetch.go
index f2c6338e..bdf99b8a 100644
--- a/plugin/table_fetch.go
+++ b/plugin/table_fetch.go
@@ -66,7 +66,7 @@ func (t *Table) executeGetCall(ctx context.Context, queryData *QueryData) (err e
 		// we can now close the item chan
 		queryData.fetchComplete(ctx)
 		if r := recover(); r != nil {
-			err = status.Error(codes.Internal, fmt.Sprintf("get call %s failed with panic %v", t.Get.namedHydrate.Name, r))
+			err = status.Error(codes.Internal, fmt.Sprintf("get call %s failed with panic %v", t.Get.NamedHydrate.Name, r))
 		}
 	}()
 
@@ -179,7 +179,7 @@ func (t *Table) doGetForQualValues(ctx context.Context, queryData *QueryData, ke
 // execute a get call for a single key column qual value
 // if a matrix is defined, call for every matrix item
 func (t *Table) doGet(ctx context.Context, queryData *QueryData) (err error) {
-	hydrateKey := t.Get.namedHydrate.Name
+	hydrateKey := t.Get.NamedHydrate.Name
 	defer func() {
 		if p := recover(); p != nil {
 			err = status.Error(codes.Internal, fmt.Sprintf("table '%s': Get hydrate call %s failed with panic %v", t.Name, hydrateKey, p))
@@ -228,7 +228,7 @@ func (t *Table) get(ctx context.Context, queryData *QueryData) (*rowData, error)
 	rd := newRowData(queryData, nil)
 	// just invoke callHydrateWithRetries()
 	var getItem any
-	getItem, err := rd.callHydrateWithRetries(ctx, queryData, t.Get.namedHydrate, t.Get.IgnoreConfig, t.Get.RetryConfig)
+	getItem, err := rd.callHydrateWithRetries(ctx, queryData, t.Get.NamedHydrate, t.Get.IgnoreConfig, t.Get.RetryConfig)
 	rd.item = getItem
 	return rd, err
 }
@@ -280,7 +280,7 @@ func (t *Table) getForEachMatrixItem(ctx context.Context, queryData *QueryData)
 			matrixRd := newRowData(matrixQueryData, nil)
 
 			// now call hydrate from the matrix rowdata
-			item, err := matrixRd.callHydrateWithRetries(fetchContext, matrixQueryData, t.Get.namedHydrate, t.Get.IgnoreConfig, t.Get.RetryConfig)
+			item, err := matrixRd.callHydrateWithRetries(fetchContext, matrixQueryData, t.Get.NamedHydrate, t.Get.IgnoreConfig, t.Get.RetryConfig)
 
 			if err != nil {
 				log.Printf("[WARN] callHydrateWithRetries returned error %v", err)
@@ -358,7 +358,7 @@ func (t *Table) executeListCall(ctx context.Context, queryData *QueryData) {
 	defer log.Printf("[TRACE] executeListCall COMPLETE (%s)", queryData.connectionCallId)
 	defer func() {
 		if r := recover(); r != nil {
-			queryData.streamError(status.Error(codes.Internal, fmt.Sprintf("list call %s failed with panic %v", t.List.namedHydrate.Name, r)))
+			queryData.streamError(status.Error(codes.Internal, fmt.Sprintf("list call %s failed with panic %v", t.List.NamedHydrate.Name, r)))
 		}
 		// list call will return when it has streamed all items so close rowDataChan
 		queryData.fetchComplete(ctx)
@@ -373,13 +373,13 @@ func (t *Table) executeListCall(ctx context.Context, queryData *QueryData) {
 	}
 
 	// invoke list call - hydrateResults is nil as list call does not use it (it must comply with HydrateFunc signature)
-	var childHydrate *namedHydrateFunc = nil
-	listCall := t.List.namedHydrate
+	var childHydrate NamedHydrateFunc
+	listCall := t.List.NamedHydrate
 	// if there is a parent hydrate function, call that
 	// - the child 'Hydrate' function will be called by QueryData.StreamListItem,
 	if t.List.ParentHydrate != nil {
-		listCall = t.List.namedParentHydrate
-		childHydrate = t.List.namedHydrate
+		listCall = t.List.NamedParentHydrate
+		childHydrate = t.List.NamedHydrate
 	}
 
 	// store the list call and child hydrate call - these will be used later when we call setListMetadata
@@ -390,11 +390,11 @@ func (t *Table) executeListCall(ctx context.Context, queryData *QueryData) {
 	if listQual := t.getListCallQualValueList(queryData); listQual != nil {
 		log.Printf("[TRACE] one qual with list value will be processed: %v", *listQual)
 		qualValueList := listQual.Value.GetListValue()
-		t.doListForQualValues(ctx, queryData, listQual.Column, qualValueList, *listCall)
+		t.doListForQualValues(ctx, queryData, listQual.Column, qualValueList, listCall)
 		return
 	}
 
-	t.doList(ctx, queryData, *listCall)
+	t.doList(ctx, queryData, listCall)
 }
 
 // if this table defines key columns, and if there is a SINGLE qual with a list value
@@ -448,7 +448,7 @@ func (t *Table) getListCallQualValueList(queryData *QueryData) *quals.Qual {
 }
 
 // doListForQualValues is called when there is an equals qual and the qual value is a list of values
-func (t *Table) doListForQualValues(ctx context.Context, queryData *QueryData, keyColumn string, qualValueList *proto.QualValueList, listCall namedHydrateFunc) {
+func (t *Table) doListForQualValues(ctx context.Context, queryData *QueryData, keyColumn string, qualValueList *proto.QualValueList, listCall NamedHydrateFunc) {
 	var listWg sync.WaitGroup
 
 	log.Printf("[TRACE] doListForQualValues - qual value is a list - executing list for each qual value item, qualValueList: %v", qualValueList)
@@ -473,7 +473,7 @@ func (t *Table) doListForQualValues(ctx context.Context, queryData *QueryData, k
 	listWg.Wait()
 }
 
-func (t *Table) doList(ctx context.Context, queryData *QueryData, listCall namedHydrateFunc) {
+func (t *Table) doList(ctx context.Context, queryData *QueryData, listCall NamedHydrateFunc) {
 	ctx, span := telemetry.StartSpan(ctx, t.Plugin.Name, "Table.doList (%s)", t.Name)
 	defer span.End()
 
@@ -510,7 +510,7 @@ func (t *Table) doList(ctx context.Context, queryData *QueryData, listCall named
 
 // ListForEach executes the provided list call for each of a set of matrixItem
 // enables multi-partition fetching
-func (t *Table) listForEachMatrixItem(ctx context.Context, queryData *QueryData, listCall namedHydrateFunc) {
+func (t *Table) listForEachMatrixItem(ctx context.Context, queryData *QueryData, listCall NamedHydrateFunc) {
 	ctx, span := telemetry.StartSpan(ctx, t.Plugin.Name, "Table.listForEachMatrixItem (%s)", t.Name)
 	// TODO add matrix item to span
 	defer span.End()
diff --git a/plugin/table_schema.go b/plugin/table_schema.go
index 33bd3c04..44cf214c 100644
--- a/plugin/table_schema.go
+++ b/plugin/table_schema.go
@@ -43,8 +43,7 @@ func (t *Table) GetSchema() (*proto.TableSchema, error) {
 				Description: column.Description,
 			}
 			if column.Hydrate != nil {
-				name, _ := column.Hydrate.getOriginalFuncName()
-				columnDef.Hydrate = name
+				columnDef.Hydrate = column.NamedHydrate.Name
 			}
 			if !helpers.IsNil(column.Default) {
 				// to convert the column default to a proto.Column, call ToColumnValue with a nil value
diff --git a/plugin/table_test.go b/plugin/table_test.go
index 816e210c..e5f87329 100644
--- a/plugin/table_test.go
+++ b/plugin/table_test.go
@@ -220,7 +220,7 @@ var testCasesRequiredHydrateCalls = map[string]requiredHydrateCallsTest{
 		columns:   []string{"c1"},
 		fetchType: fetchTypeList,
 		expected: []*hydrateCall{
-			{namedHydrateFunc: namedHydrateFunc{Func: hydrate1, Name: "hydrate1"}},
+			{NamedHydrateFunc: NamedHydrateFunc{Func: hydrate1, Name: "hydrate1"}},
 		},
 	},
 	"list - 1 hydrate, depends [HydrateDependencies]": {
@@ -238,8 +238,8 @@ var testCasesRequiredHydrateCalls = map[string]requiredHydrateCallsTest{
 		columns:   []string{"c1"},
 		fetchType: fetchTypeList,
 		expected: []*hydrateCall{
-			{namedHydrateFunc: namedHydrateFunc{Func: hydrate1, Name: "hydrate1"}, Depends: []namedHydrateFunc{{Name: "hydrate2"}}},
-			{namedHydrateFunc: namedHydrateFunc{Func: hydrate2, Name: "hydrate2"}},
+			{NamedHydrateFunc: NamedHydrateFunc{Func: hydrate1, Name: "hydrate1"}, Depends: []NamedHydrateFunc{{Name: "hydrate2"}}},
+			{NamedHydrateFunc: NamedHydrateFunc{Func: hydrate2, Name: "hydrate2"}},
 		},
 	},
 	"get - 2 hydrate, depends [HydrateDependencies]": {
@@ -258,9 +258,9 @@ var testCasesRequiredHydrateCalls = map[string]requiredHydrateCallsTest{
 		columns:   []string{"c1", "c2"},
 		fetchType: fetchTypeGet,
 		expected: []*hydrateCall{
-			{namedHydrateFunc: namedHydrateFunc{Func: hydrate1, Name: "hydrate1"}, Depends: []namedHydrateFunc{{Name: "hydrate3"}}},
-			{namedHydrateFunc: namedHydrateFunc{Func: hydrate3, Name: "hydrate3"}},
-			{namedHydrateFunc: namedHydrateFunc{Func: hydrate2, Name: "hydrate2"}}},
+			{NamedHydrateFunc: NamedHydrateFunc{Func: hydrate1, Name: "hydrate1"}, Depends: []NamedHydrateFunc{{Name: "hydrate3"}}},
+			{NamedHydrateFunc: NamedHydrateFunc{Func: hydrate3, Name: "hydrate3"}},
+			{NamedHydrateFunc: NamedHydrateFunc{Func: hydrate2, Name: "hydrate2"}}},
 	},
 	"get - 2 depends [HydrateDependencies]": {
 		table: &Table{
@@ -281,9 +281,9 @@ var testCasesRequiredHydrateCalls = map[string]requiredHydrateCallsTest{
 		columns:   []string{"c1"},
 		fetchType: fetchTypeGet,
 		expected: []*hydrateCall{
-			{namedHydrateFunc: namedHydrateFunc{Func: hydrate1, Name: "hydrate1"}, Depends: []namedHydrateFunc{{Name: "hydrate2"}}},
-			{namedHydrateFunc: namedHydrateFunc{Func: hydrate2, Name: "hydrate2"}, Depends: []namedHydrateFunc{{Name: "hydrate3"}}},
-			{namedHydrateFunc: namedHydrateFunc{Func: hydrate3, Name: "hydrate3"}}},
+			{NamedHydrateFunc: NamedHydrateFunc{Func: hydrate1, Name: "hydrate1"}, Depends: []NamedHydrateFunc{{Name: "hydrate2"}}},
+			{NamedHydrateFunc: NamedHydrateFunc{Func: hydrate2, Name: "hydrate2"}, Depends: []NamedHydrateFunc{{Name: "hydrate3"}}},
+			{NamedHydrateFunc: NamedHydrateFunc{Func: hydrate3, Name: "hydrate3"}}},
 	},
 	"get - unreferenced depends [HydrateDependencies]": {
 		table: &Table{
@@ -304,7 +304,7 @@ var testCasesRequiredHydrateCalls = map[string]requiredHydrateCallsTest{
 		columns:   []string{"c3"},
 		fetchType: fetchTypeGet,
 		expected: []*hydrateCall{
-			{namedHydrateFunc: namedHydrateFunc{Func: hydrate3, Name: "hydrate3"}}},
+			{NamedHydrateFunc: NamedHydrateFunc{Func: hydrate3, Name: "hydrate3"}}},
 	},
 
 	"list - 1 hydrate, depends": {
@@ -322,8 +322,8 @@ var testCasesRequiredHydrateCalls = map[string]requiredHydrateCallsTest{
 		columns:   []string{"c1"},
 		fetchType: fetchTypeList,
 		expected: []*hydrateCall{
-			{namedHydrateFunc: namedHydrateFunc{Func: hydrate1, Name: "hydrate1"}, Depends: []namedHydrateFunc{{Name: "hydrate2"}}},
-			{namedHydrateFunc: namedHydrateFunc{Func: hydrate2, Name: "hydrate2"}}},
+			{NamedHydrateFunc: NamedHydrateFunc{Func: hydrate1, Name: "hydrate1"}, Depends: []NamedHydrateFunc{{Name: "hydrate2"}}},
+			{NamedHydrateFunc: NamedHydrateFunc{Func: hydrate2, Name: "hydrate2"}}},
 	},
 	"get - 2 hydrate, depends": {
 		table: &Table{
@@ -341,9 +341,9 @@ var testCasesRequiredHydrateCalls = map[string]requiredHydrateCallsTest{
 		columns:   []string{"c1", "c2"},
 		fetchType: fetchTypeGet,
 		expected: []*hydrateCall{
-			{namedHydrateFunc: namedHydrateFunc{Func: hydrate1, Name: "hydrate1"}, Depends: []namedHydrateFunc{{Name: "hydrate3"}}},
-			{namedHydrateFunc: namedHydrateFunc{Func: hydrate3, Name: "hydrate3"}},
-			{namedHydrateFunc: namedHydrateFunc{Func: hydrate2, Name: "hydrate2"}},
+			{NamedHydrateFunc: NamedHydrateFunc{Func: hydrate1, Name: "hydrate1"}, Depends: []NamedHydrateFunc{{Name: "hydrate3"}}},
+			{NamedHydrateFunc: NamedHydrateFunc{Func: hydrate3, Name: "hydrate3"}},
+			{NamedHydrateFunc: NamedHydrateFunc{Func: hydrate2, Name: "hydrate2"}},
 		},
 	},
 	"get - 2 depends": {
@@ -365,9 +365,9 @@ var testCasesRequiredHydrateCalls = map[string]requiredHydrateCallsTest{
 		columns:   []string{"c1"},
 		fetchType: fetchTypeGet,
 		expected: []*hydrateCall{
-			{namedHydrateFunc: namedHydrateFunc{Func: hydrate1, Name: "hydrate1"}, Depends: []namedHydrateFunc{{Name: "hydrate2"}}},
-			{namedHydrateFunc: namedHydrateFunc{Func: hydrate2, Name: "hydrate2"}, Depends: []namedHydrateFunc{{Name: "hydrate3"}}},
-			{namedHydrateFunc: namedHydrateFunc{Func: hydrate3, Name: "hydrate3"}},
+			{NamedHydrateFunc: NamedHydrateFunc{Func: hydrate1, Name: "hydrate1"}, Depends: []NamedHydrateFunc{{Name: "hydrate2"}}},
+			{NamedHydrateFunc: NamedHydrateFunc{Func: hydrate2, Name: "hydrate2"}, Depends: []NamedHydrateFunc{{Name: "hydrate3"}}},
+			{NamedHydrateFunc: NamedHydrateFunc{Func: hydrate3, Name: "hydrate3"}},
 		},
 	},
 	"get - unreferenced depends": {
@@ -388,7 +388,7 @@ var testCasesRequiredHydrateCalls = map[string]requiredHydrateCallsTest{
 		},
 		columns:   []string{"c3"},
 		fetchType: fetchTypeGet,
-		expected:  []*hydrateCall{{namedHydrateFunc: namedHydrateFunc{Func: hydrate3, Name: "hydrate3"}}},
+		expected:  []*hydrateCall{{NamedHydrateFunc: NamedHydrateFunc{Func: hydrate3, Name: "hydrate3"}}},
 	},
 }
 
@@ -397,8 +397,13 @@ func TestRequiredHydrateCalls(t *testing.T) {
 	logger := hclog.NewNullLogger()
 	log.SetOutput(logger.StandardWriter(&hclog.StandardLoggerOptions{InferLevels: true}))
 
+	var targetTest string
+	//targetTest = "list - 1 hydrate, depends"
 	plugin.initialise(logger)
 	for name, test := range testCasesRequiredHydrateCalls {
+		if targetTest != "" && name != targetTest {
+			continue
+		}
 		test.table.initialise(plugin)
 
 		d, _ := newTestQueryData(plugin, &QueryContext{Columns: test.columns}, test.table, test.fetchType)
diff --git a/plugin/table_validate.go b/plugin/table_validate.go
index 205398ff..77648d24 100644
--- a/plugin/table_validate.go
+++ b/plugin/table_validate.go
@@ -135,8 +135,8 @@ func (t *Table) detectCyclicHydrateDependencies() string {
 	var dependencyGraph = topsort.NewGraph()
 	dependencyGraph.AddNode("root")
 
-	updateDependencyGraph := func(hydrateFunc HydrateFunc, hydrateDepends []HydrateFunc) {
-		name := newNamedHydrateFunc(hydrateFunc).Name
+	updateDependencyGraph := func(namedHydrateFunc NamedHydrateFunc, hydrateDepends []HydrateFunc) {
+		name := namedHydrateFunc.Name
 		if !dependencyGraph.ContainsNode(name) {
 			dependencyGraph.AddNode(name)
 		}
@@ -151,7 +151,7 @@ func (t *Table) detectCyclicHydrateDependencies() string {
 	}
 
 	for _, hydrateConfig := range t.hydrateConfigMap {
-		updateDependencyGraph(hydrateConfig.Func, hydrateConfig.Depends)
+		updateDependencyGraph(hydrateConfig.namedHydrate, hydrateConfig.Depends)
 	}
 
 	if _, err := dependencyGraph.TopSort("root"); err != nil {
diff --git a/query_cache/set_request_subscriber.go b/query_cache/set_request_subscriber.go
index 8a57905f..3f3fa201 100644
--- a/query_cache/set_request_subscriber.go
+++ b/query_cache/set_request_subscriber.go
@@ -126,7 +126,7 @@ func (s *setRequestSubscriber) readAndStreamAsync(ctx context.Context) (chan str
 				}
 			}
 
-			log.Printf("[INFO] readAndStreamAsync streaming complete (rows streamed %d) (%s)", s.rowsStreamed, s.callId)
+			log.Printf("[TRACE] readAndStreamAsync streaming complete (rows streamed %d) (%s)", s.rowsStreamed, s.callId)
 		}
 
 	}()