Skip to content

Commit ab3065a

Browse files
authored
Merge pull request #1076 from dolthub/daylon/oid
Added Internal ID representation
2 parents 8138392 + 42d1ef1 commit ab3065a

File tree

166 files changed

+7019
-2545
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

166 files changed

+7019
-2545
lines changed

core/id/cache.go

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
// Copyright 2024 Dolthub, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package id
16+
17+
import (
18+
"hash/crc32"
19+
"strconv"
20+
"sync"
21+
)
22+
23+
// builtinOidLimit is the largest OID that Postgres will assign to built-in items, so we use this to mitigate conflicts
24+
// with existing and future built-in OIDs.
25+
const builtinOidLimit = 65535
26+
27+
var (
28+
// crcTable is the table that is used for our CRC operations.
29+
crcTable = crc32.MakeTable(crc32.Castagnoli)
30+
// globalCache is the cache structure that is used for the server session.
31+
globalCache = &cacheStruct{
32+
mutex: &sync.RWMutex{},
33+
toOID: map[Internal]uint32{Null: 0},
34+
toInternal: map[uint32]Internal{0: Null},
35+
}
36+
)
37+
38+
// cacheStruct is the cache structure that holds mappings between the Internal ID and external OID (used by Postgres).
39+
// The mappings are temporary, and exist only within a server session. We must discourage users from storing converted
40+
// OIDs, and to use the actual OID type, since the type uses Internal IDs so long as it's not returned to the user.
41+
type cacheStruct struct {
42+
mutex *sync.RWMutex
43+
toOID map[Internal]uint32
44+
toInternal map[uint32]Internal
45+
}
46+
47+
// Cache returns the global cache that is used for the server session.
48+
func Cache() *cacheStruct {
49+
return globalCache
50+
}
51+
52+
// ToOID returns the OID associated with the given Internal ID.
53+
func (cache *cacheStruct) ToOID(id Internal) uint32 {
54+
// If the ID is in the cache, then we can just return its associated OID
55+
cache.mutex.RLock()
56+
if oid, ok := cache.toOID[id]; ok {
57+
cache.mutex.RUnlock()
58+
return oid
59+
}
60+
cache.mutex.RUnlock()
61+
if id.Section() == Section_OID {
62+
oid, _ := strconv.ParseUint(id.Segment(0), 10, 32)
63+
return uint32(oid)
64+
}
65+
cache.mutex.Lock()
66+
defer cache.mutex.Unlock()
67+
underlyingBytes := id.UnderlyingBytes()
68+
oid := crc32.Checksum(underlyingBytes, crcTable)
69+
// If the generated OID is valid, then we'll add it to the cache and return it
70+
if _, ok := cache.toInternal[oid]; !ok && oid > builtinOidLimit {
71+
cache.toOID[id] = oid
72+
cache.toInternal[oid] = id
73+
return oid
74+
}
75+
// In this case, the OID is not valid, so we'll run a small loop to generate an OID based on the actual ID.
76+
// This retains some level of determinism for OID to ID relationships.
77+
modifiedBytes := make([]byte, len(underlyingBytes)+1)
78+
copy(modifiedBytes[1:], underlyingBytes)
79+
for i := byte(0); i < 255; i++ {
80+
modifiedBytes[0] = i
81+
oid = crc32.Checksum(underlyingBytes, crcTable)
82+
if _, ok := cache.toInternal[oid]; !ok && oid > builtinOidLimit {
83+
cache.toOID[id] = oid
84+
cache.toInternal[oid] = id
85+
return oid
86+
}
87+
}
88+
// If we're here, then we'll just search for an empty OID as a last resort
89+
for i := uint32(4294967295); i > builtinOidLimit; i-- {
90+
if _, ok := cache.toInternal[oid]; !ok {
91+
cache.toOID[id] = oid
92+
cache.toInternal[oid] = id
93+
return oid
94+
}
95+
}
96+
// We must have over 4 billion items in the database, so we'll panic since there's nothing we can do
97+
panic("all OIDs have been taken")
98+
}
99+
100+
// ToInternal returns the Internal ID associated with the given OID.
101+
func (cache *cacheStruct) ToInternal(oid uint32) Internal {
102+
cache.mutex.RLock()
103+
defer cache.mutex.RUnlock()
104+
if id, ok := cache.toInternal[oid]; ok {
105+
return id
106+
}
107+
// The OID is not in the cache, so it's invalid
108+
return ""
109+
}
110+
111+
// Exists returns whether the given Internal ID exists within the cache. This should primarily be used for the default
112+
// functions, as it's not guaranteed that user functions will be in the cache, especially after a server restart.
113+
func (cache *cacheStruct) Exists(id Internal) bool {
114+
cache.mutex.RLock()
115+
defer cache.mutex.RUnlock()
116+
_, ok := cache.toOID[id]
117+
return ok
118+
}
119+
120+
// setBuiltIn sets the given ID to the OID. This should only be used for the built-in items.
121+
func (cache *cacheStruct) setBuiltIn(id Internal, oid uint32) {
122+
if oid > builtinOidLimit {
123+
panic("oid is not a built-in")
124+
}
125+
cache.toOID[id] = oid
126+
cache.toInternal[oid] = id
127+
}
128+
129+
// update is used to change the OID mapping of an existing Internal ID that has been changed (where the Internal ID
130+
// points to the same logical item).
131+
//
132+
//lint:ignore U1000 For future use
133+
func (cache *cacheStruct) update(old Internal, new Internal) {
134+
cache.mutex.Lock()
135+
defer cache.mutex.Unlock()
136+
// If the old ID doesn't exist in the cache, then we don't have anything to update
137+
oid, ok := cache.toOID[old]
138+
if !ok {
139+
return
140+
}
141+
// We'll delete the old entry and add the new entry, keeping the OID the same for the server session
142+
delete(cache.toOID, old)
143+
delete(cache.toInternal, oid)
144+
cache.toOID[new] = oid
145+
cache.toInternal[oid] = new
146+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright 2024 Dolthub, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package id
16+
17+
// This adds all of the built-in schemas to the cache.
18+
func init() {
19+
globalCache.setBuiltIn(NewInternal(Section_AccessMethod, "heap"), 2)
20+
globalCache.setBuiltIn(NewInternal(Section_AccessMethod, "btree"), 403)
21+
globalCache.setBuiltIn(NewInternal(Section_AccessMethod, "hash"), 405)
22+
globalCache.setBuiltIn(NewInternal(Section_AccessMethod, "gist"), 783)
23+
globalCache.setBuiltIn(NewInternal(Section_AccessMethod, "gin"), 2742)
24+
globalCache.setBuiltIn(NewInternal(Section_AccessMethod, "brin"), 3580)
25+
globalCache.setBuiltIn(NewInternal(Section_AccessMethod, "spgist"), 4000)
26+
}

0 commit comments

Comments
 (0)