dolthub--doltgresql
251 行
7.8 KiB
Go
251 行
7.8 KiB
Go
// Copyright 2024 Dolthub, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package pgcatalog
|
|
|
|
import (
|
|
"github.com/cockroachdb/errors"
|
|
"github.com/dolthub/go-mysql-server/sql"
|
|
|
|
"github.com/dolthub/doltgresql/core"
|
|
"github.com/dolthub/doltgresql/core/id"
|
|
"github.com/dolthub/doltgresql/server/functions"
|
|
)
|
|
|
|
// pgNamespace represents a row in the pg_namespace table.
|
|
// We store oids in their native format as well so that we can do range scans on them.
|
|
type pgNamespace struct {
|
|
oid id.Id
|
|
oidNative uint32
|
|
name string
|
|
}
|
|
|
|
// pgCatalogCache is a session cache that stores the contents of pg_catalog tables. Since this cache instance is only
|
|
// ever used by a single session, it does not include any synchronization for concurrent data access. A pgCatalogCache
|
|
// only caches data for the length of a single query, using |pid| to identify the current query. This means that the
|
|
// initial read of data from a pg_catalog table will always be generated fresh, then stored in this cache for any other
|
|
// table reads needed as part of that same query.
|
|
type pgCatalogCache struct {
|
|
// pid marks the process id for the query this cache data represents. pgCatalogCache instances currently only
|
|
// cache data within the context of a single query – not across multiple queries.
|
|
pid uint64
|
|
|
|
// pg_classes
|
|
pgClasses *pgClassCache
|
|
|
|
// pg_type
|
|
pgTypes *pgTypeCache
|
|
|
|
// pg_constraints
|
|
pgConstraints *pgConstraintCache
|
|
|
|
// pg_namespace
|
|
pgNamespaces *pgNamespaceCache
|
|
|
|
// pg_attribute
|
|
pgAttributes *pgAttributeCache
|
|
|
|
// pg_index / pg_indexes
|
|
pgIndexes *pgIndexCache
|
|
|
|
// pg_sequence / pg_sequences
|
|
sequences []*pgSequence
|
|
|
|
// pg_attrdef
|
|
attrdefCols []functions.ItemColumnDefault
|
|
attrdefTableOIDs []id.Id
|
|
|
|
// pg_views
|
|
views []sql.ViewDefinition
|
|
viewSchemas []string
|
|
|
|
// pg_tables
|
|
tables []pgTableRow
|
|
}
|
|
|
|
// pgClassCache holds cached data for the pg_class table, including two btree indexes for fast lookups by OID and
|
|
// by relname
|
|
type pgClassCache struct {
|
|
classes []*pgClass
|
|
nameIdx *inMemIndexStorage[*pgClass]
|
|
oidIdx *inMemIndexStorage[*pgClass]
|
|
}
|
|
|
|
// getIndex implements BTreeStorageAccess.
|
|
func (p pgClassCache) getIndex(name string) *inMemIndexStorage[*pgClass] {
|
|
switch name {
|
|
case "pg_class_oid_index":
|
|
return p.oidIdx
|
|
case "pg_class_relname_nsp_index":
|
|
return p.nameIdx
|
|
default:
|
|
panic("unknown pg_class index: " + name)
|
|
}
|
|
}
|
|
|
|
var _ BTreeStorageAccess[*pgClass] = &pgClassCache{}
|
|
|
|
// pgTypeCache holds cached data for the pg_type table, including two btree indexes for fast lookups by OID and
|
|
// by typname
|
|
type pgTypeCache struct {
|
|
types []*pgType
|
|
nameIdx *inMemIndexStorage[*pgType]
|
|
oidIdx *inMemIndexStorage[*pgType]
|
|
}
|
|
|
|
// getIndex implements BTreeStorageAccess.
|
|
func (p pgTypeCache) getIndex(name string) *inMemIndexStorage[*pgType] {
|
|
switch name {
|
|
case pgTypeOidIndex:
|
|
return p.oidIdx
|
|
case pgTypnameIndex:
|
|
return p.nameIdx
|
|
default:
|
|
panic("unknown pg_type index: " + name)
|
|
}
|
|
}
|
|
|
|
var _ BTreeStorageAccess[*pgType] = &pgTypeCache{}
|
|
|
|
// pgIndexCache holds cached data for the pg_index table, including two btree indexes for fast lookups by index OID
|
|
type pgIndexCache struct {
|
|
indexes []*pgIndex
|
|
tableNames map[id.Id]string
|
|
indexOidIdx *inMemIndexStorage[*pgIndex]
|
|
indrelidIdx *inMemIndexStorage[*pgIndex]
|
|
}
|
|
|
|
var _ BTreeStorageAccess[*pgIndex] = &pgIndexCache{}
|
|
|
|
// pgConstraintCache holds cached data for the pg_constraint table, including three btree indexes for fast lookups
|
|
type pgConstraintCache struct {
|
|
constraints []*pgConstraint
|
|
oidIdx *inMemIndexStorage[*pgConstraint]
|
|
relidTypNameIdx *inMemIndexStorage[*pgConstraint]
|
|
nameSchemaIdx *inMemIndexStorage[*pgConstraint]
|
|
typIdx *inMemIndexStorage[*pgConstraint]
|
|
}
|
|
|
|
var _ BTreeStorageAccess[*pgConstraint] = &pgConstraintCache{}
|
|
|
|
// getIndex implements BTreeStorageAccess.
|
|
func (p pgConstraintCache) getIndex(name string) *inMemIndexStorage[*pgConstraint] {
|
|
switch name {
|
|
case "pg_constraint_oid_index":
|
|
return p.oidIdx
|
|
case "pg_constraint_conrelid_contypid_conname_index":
|
|
return p.relidTypNameIdx
|
|
case "pg_constraint_conname_nsp_index":
|
|
return p.nameSchemaIdx
|
|
case "pg_constraint_contypid_index":
|
|
return p.typIdx
|
|
default:
|
|
panic("unknown pg_constraint index: " + name)
|
|
}
|
|
}
|
|
|
|
// pgAttributeCache holds cached data for the pg_attribute table, including two btree indexes for fast lookups
|
|
type pgAttributeCache struct {
|
|
attributes []*pgAttribute
|
|
attrelidIdx *inMemIndexStorage[*pgAttribute]
|
|
attrelidAttnameIdx *inMemIndexStorage[*pgAttribute]
|
|
}
|
|
|
|
var _ BTreeStorageAccess[*pgAttribute] = &pgAttributeCache{}
|
|
|
|
// pgNamespaceCache holds cached data for the pg_namespace table, including two btree indexes for fast lookups by OID and by name
|
|
type pgNamespaceCache struct {
|
|
namespaces []*pgNamespace
|
|
oidIdx *inMemIndexStorage[*pgNamespace]
|
|
nameIdx *inMemIndexStorage[*pgNamespace]
|
|
}
|
|
|
|
var _ BTreeStorageAccess[*pgNamespace] = &pgNamespaceCache{}
|
|
|
|
// getIndex implements BTreeStorageAccess.
|
|
func (p pgNamespaceCache) getIndex(name string) *inMemIndexStorage[*pgNamespace] {
|
|
switch name {
|
|
case "pg_namespace_oid_index":
|
|
return p.oidIdx
|
|
case "pg_namespace_nspname_index":
|
|
return p.nameIdx
|
|
default:
|
|
panic("unknown pg_namespace index: " + name)
|
|
}
|
|
}
|
|
|
|
func (p pgAttributeCache) getIndex(name string) *inMemIndexStorage[*pgAttribute] {
|
|
switch name {
|
|
case "pg_attribute_relid_attnum_index":
|
|
return p.attrelidIdx
|
|
case "pg_attribute_relid_attnam_index":
|
|
return p.attrelidAttnameIdx
|
|
default:
|
|
panic("unknown pg_attribute index: " + name)
|
|
}
|
|
}
|
|
|
|
// getIndex implements BTreeStorageAccess.
|
|
func (p pgIndexCache) getIndex(name string) *inMemIndexStorage[*pgIndex] {
|
|
switch name {
|
|
case "pg_index_indexrelid_index":
|
|
return p.indexOidIdx
|
|
case "pg_index_indrelid_index":
|
|
return p.indrelidIdx
|
|
default:
|
|
panic("unknown pg_index index: " + name)
|
|
}
|
|
}
|
|
|
|
// newPgCatalogCache creates a new pgCatalogCache, with the query/process ID set to |pid|. The PID is important,
|
|
// since pgCatalogCache instances only cache data for the duration of a single query.
|
|
func newPgCatalogCache(pid uint64) *pgCatalogCache {
|
|
return &pgCatalogCache{
|
|
pid: pid,
|
|
}
|
|
}
|
|
|
|
// getPgCatalogCache returns the pgCatalogCache instance for the current query in this session. If no cache exists
|
|
// yet, then a new one is created and returned. Note that pgCatalogCache only caches catalog data for a single query,
|
|
// so if the PID of the current context does not match the PID of the context when the pgCatalogCache was created,
|
|
// then a new cache will be created.
|
|
func getPgCatalogCache(ctx *sql.Context) (*pgCatalogCache, error) {
|
|
untypedPgCatalogCache, err := core.GetPgCatalogCache(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if untypedPgCatalogCache == nil {
|
|
return initializeNewPgCatalogCache(ctx)
|
|
}
|
|
|
|
cache, ok := untypedPgCatalogCache.(*pgCatalogCache)
|
|
if !ok {
|
|
return nil, errors.Errorf("unexpected type %T for pg_catalog cache", untypedPgCatalogCache)
|
|
}
|
|
if cache.pid != ctx.Pid() {
|
|
return initializeNewPgCatalogCache(ctx)
|
|
}
|
|
return cache, nil
|
|
}
|
|
|
|
// initializeNewPgCatalogCache creates a new pgCatalogCache instance and sets it in the context. This function should
|
|
// not be used directly, and should only be used directly by getPgCatalogCache(*sql.Context).
|
|
func initializeNewPgCatalogCache(ctx *sql.Context) (*pgCatalogCache, error) {
|
|
cache := newPgCatalogCache(ctx.Pid())
|
|
if err := core.SetPgCatalogCache(ctx, cache); err != nil {
|
|
return nil, err
|
|
}
|
|
return cache, nil
|
|
}
|