Line data Source code
1 : /*
2 : * SPDX-License-Identifier: MPL-2.0
3 : *
4 : * This Source Code Form is subject to the terms of the Mozilla Public
5 : * License, v. 2.0. If a copy of the MPL was not distributed with this
6 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 : *
8 : * Copyright 2024 MonetDB Foundation;
9 : * Copyright August 2008 - 2023 MonetDB B.V.;
10 : * Copyright 1997 - July 2008 CWI.
11 : */
12 :
13 : #include "monetdb_config.h"
14 : #include "store_sequence.h"
15 : #include "sql_storage.h"
16 :
17 : void
18 1804 : sequences_lock(sql_store Store)
19 : {
20 1804 : sqlstore *store = Store;
21 1804 : MT_lock_set(&store->column_locks[NR_COLUMN_LOCKS-1]);
22 1804 : }
23 :
24 : void
25 1804 : sequences_unlock(sql_store Store)
26 : {
27 1804 : sqlstore *store = Store;
28 1804 : MT_lock_unset(&store->column_locks[NR_COLUMN_LOCKS-1]);
29 1804 : }
30 :
31 : typedef struct store_sequence {
32 : sqlid seqid;
33 : lng cur;
34 : bool intrans;
35 : } store_sequence;
36 :
37 : void
38 764 : log_store_sequence(sql_store Store, void *s)
39 : {
40 764 : sqlstore *store = Store;
41 764 : store_sequence *seq = s;
42 764 : store->logger_api.log_tsequence(store, seq->seqid, seq->cur);
43 764 : seq->intrans = false;
44 764 : }
45 :
46 : int
47 173 : seq_hash(void *s)
48 : {
49 173 : store_sequence *seq = s;
50 173 : return seq->seqid;
51 : }
52 :
53 : static void
54 173 : sequence_destroy( void *dummy, store_sequence *s )
55 : {
56 173 : (void)dummy;
57 173 : _DELETE(s);
58 173 : }
59 :
60 : void
61 326 : seq_hash_destroy( sql_hash *h )
62 : {
63 326 : if (h == NULL || h->sa)
64 : return ;
65 10725 : for (int i = 0; i < h->size; i++) {
66 10400 : sql_hash_e *e = h->buckets[i];
67 :
68 10573 : while (e) {
69 173 : sql_hash_e *next = e->chain;
70 :
71 173 : sequence_destroy(NULL, e->value);
72 173 : _DELETE(e);
73 173 : e = next;
74 : }
75 : }
76 325 : _DELETE(h->buckets);
77 325 : _DELETE(h);
78 : }
79 :
80 : static store_sequence *
81 1175 : sequence_lookup( sql_hash *h, sqlid id)
82 : {
83 1175 : sql_hash_e *e = h->buckets[id & (h->size-1)];
84 1182 : while(e) {
85 971 : sql_hash_e *next = e->chain;
86 971 : store_sequence *s = e->value;
87 :
88 971 : if (s->seqid == id)
89 964 : return s;
90 : e = next;
91 : }
92 : return NULL;
93 : }
94 :
95 : /* lock is held */
96 : static store_sequence *
97 1070 : update_sequence(sqlstore *store, store_sequence *s)
98 : {
99 1070 : if (!s->intrans && !list_append(store->seqchanges, s))
100 : return NULL;
101 1070 : s->intrans = true;
102 1070 : return s;
103 : }
104 :
105 : /* lock is held */
106 : static store_sequence *
107 173 : sequence_create(sqlstore *store, sql_sequence *seq )
108 : {
109 173 : lng val = 0;
110 173 : store_sequence *s = NULL;
111 173 : s = MNEW(store_sequence);
112 173 : if(!s)
113 : return NULL;
114 :
115 173 : *s = (store_sequence) {
116 173 : .seqid = seq->base.id,
117 173 : .cur = seq->start,
118 : };
119 :
120 173 : if (!isNew(seq) && store->logger_api.get_sequence(store, seq->base.id, &val ))
121 13 : s->cur = val;
122 173 : if (!hash_add(store->sequences, seq_hash(s), s)) {
123 0 : _DELETE(s);
124 0 : return NULL;
125 : }
126 : return s;
127 : }
128 :
129 : int
130 47 : seq_restart(sql_store Store, sql_sequence *seq, lng start)
131 : {
132 47 : store_sequence *s;
133 47 : sqlstore *store = Store;
134 :
135 47 : assert(!is_lng_nil(start));
136 47 : sequences_lock(store);
137 47 : s = sequence_lookup(store->sequences, seq->base.id);
138 :
139 47 : if (!s) {
140 38 : lng val = 0;
141 :
142 38 : if (isNew(seq) || !store->logger_api.get_sequence(store, seq->base.id, &val )) {
143 38 : sequences_unlock(store);
144 38 : return 1;
145 : } else {
146 0 : s = sequence_create(store, seq);
147 0 : if (!s) {
148 0 : sequences_unlock(store);
149 0 : return 0;
150 : }
151 : }
152 : }
153 9 : lng ocur = s->cur;
154 9 : s->cur = start;
155 9 : if (!update_sequence(store, s)) {
156 0 : s->cur = ocur;
157 0 : sequences_unlock(store);
158 0 : return 0;
159 : }
160 9 : sequences_unlock(store);
161 9 : return 1;
162 : }
163 :
164 : int
165 1062 : seqbulk_next_value(sql_store Store, sql_sequence *seq, lng cnt, lng* dest)
166 : {
167 1062 : store_sequence *s;
168 1062 : sqlstore *store = Store;
169 :
170 : // either dest is an array of size cnt or dest is a normal pointer and cnt == 1.
171 :
172 1062 : assert(dest);
173 :
174 1062 : sequences_lock(store);
175 1062 : s = sequence_lookup(store->sequences, seq->base.id);
176 1062 : if (!s) {
177 134 : s = sequence_create(store, seq);
178 134 : if (!s) {
179 0 : sequences_unlock(store);
180 0 : return 0;
181 : }
182 : }
183 :
184 1062 : lng min = seq->minvalue;
185 1062 : lng max = seq->maxvalue;
186 1062 : lng cur = s->cur;
187 :
188 1062 : if (!seq->cycle) {
189 1044 : if ((seq->increment > 0 && s->cur > max) ||
190 16 : (seq->increment < 0 && s->cur < min)) {
191 1 : sequences_unlock(store);
192 1 : return 0;
193 : }
194 : }
195 1061 : bool store_unlocked = false;
196 1061 : if (seq->increment > 0) {
197 1044 : lng inc = seq->increment; // new value = old value + inc;
198 :
199 1044 : if (0 < cnt && !seq->cycle && !(max > 0 && s->cur < 0)) {
200 1028 : if ((max - s->cur) >= ((cnt-1) * inc)) {
201 1028 : lng ocur = s->cur;
202 1028 : s->cur += inc * cnt;
203 :
204 1028 : if (!update_sequence(store, s)) {
205 0 : s->cur = ocur;
206 0 : sequences_unlock(store);
207 0 : return 0;
208 : }
209 1028 : sequences_unlock(store);
210 1028 : store_unlocked = true;
211 : } else {
212 0 : sequences_unlock(store);
213 0 : return 0;
214 : }
215 : }
216 2031433 : for(lng i = 0; i < cnt; i++) {
217 2030389 : dest[i] = cur;
218 2030389 : if ((GDK_lng_max - inc < cur) || ((cur += inc) > max)) {
219 : // overflow
220 7 : cur = (seq->cycle)?min:lng_nil;
221 : }
222 : }
223 : } else { // seq->increment < 0
224 17 : lng inc = -seq->increment; // new value = old value - inc;
225 :
226 17 : if (0 < cnt && !seq->cycle && !(min < 0 && s->cur > 0)) {
227 15 : if ((s->cur - min) >= ((cnt-1) * inc)) {
228 15 : lng ocur = s->cur;
229 15 : s->cur -= inc * cnt;
230 :
231 15 : if (!update_sequence(store, s)) {
232 0 : s->cur = ocur;
233 0 : sequences_unlock(store);
234 0 : return 0;
235 : }
236 15 : sequences_unlock(store);
237 15 : store_unlocked = true;
238 : } else {
239 0 : sequences_unlock(store);
240 0 : return 0;
241 : }
242 : }
243 70 : for(lng i = 0; i < cnt; i++) {
244 53 : dest[i] = cur;
245 53 : if ((-GDK_lng_max + inc > cur) || ((cur -= inc) < min)) {
246 : // underflow
247 3 : cur = (seq->cycle)?max:lng_nil;
248 : }
249 : }
250 : }
251 :
252 1061 : if (!store_unlocked) {
253 18 : lng ocur = s->cur;
254 18 : s->cur = cur;
255 :
256 18 : if (!update_sequence(store, s)) {
257 0 : s->cur = ocur;
258 0 : sequences_unlock(store);
259 0 : return 0;
260 : }
261 18 : sequences_unlock(store);
262 : }
263 : return 1;
264 : }
265 :
266 : int
267 974 : seq_next_value(sql_store store, sql_sequence *seq, lng *val)
268 : {
269 974 : return seqbulk_next_value(store, seq, 1, val);
270 : }
271 :
272 : int
273 66 : seq_get_value(sql_store Store, sql_sequence *seq, lng *val)
274 : {
275 66 : store_sequence *s;
276 66 : sqlstore *store = Store;
277 :
278 66 : *val = 0;
279 66 : sequences_lock(store);
280 66 : s = sequence_lookup(store->sequences, seq->base.id);
281 66 : if (!s) {
282 39 : s = sequence_create(store, seq);
283 39 : if (!s) {
284 0 : sequences_unlock(store);
285 0 : return 0;
286 : }
287 : }
288 66 : *val = s->cur;
289 66 : sequences_unlock(store);
290 66 : return 1;
291 : }
|