| 1 | /* |
| 2 | * This Source Code Form is subject to the terms of the Mozilla Public |
| 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
| 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
| 5 | * |
| 6 | * Copyright 1997 - July 2008 CWI, August 2008 - 2019 MonetDB B.V. |
| 7 | */ |
| 8 | |
| 9 | #include "monetdb_config.h" |
| 10 | #include "rel_select.h" |
| 11 | #include "rel_rel.h" |
| 12 | #include "rel_sequence.h" |
| 13 | #include "rel_exp.h" |
| 14 | #include "sql_privileges.h" |
| 15 | |
| 16 | char* |
| 17 | sql_next_seq_name(mvc *m) |
| 18 | { |
| 19 | sqlid id = store_next_oid(); |
| 20 | size_t len = 5 + 10; /* max nr of digits of (4 bytes) int is 10 */ |
| 21 | char *msg = sa_alloc(m->sa, len); |
| 22 | |
| 23 | snprintf(msg, len, "seq_%d" , id); |
| 24 | return msg; |
| 25 | } |
| 26 | |
| 27 | static sql_rel * |
| 28 | rel_drop_seq(sql_allocator *sa, char *sname, char *seqname) |
| 29 | { |
| 30 | sql_rel *rel = rel_create(sa); |
| 31 | list *exps = new_exp_list(sa); |
| 32 | if(!rel || !exps) |
| 33 | return NULL; |
| 34 | |
| 35 | append(exps, exp_atom_clob(sa, sname)); |
| 36 | append(exps, exp_atom_clob(sa, seqname)); |
| 37 | append(exps, exp_atom_int(sa, 0)); |
| 38 | rel->l = NULL; |
| 39 | rel->r = NULL; |
| 40 | rel->op = op_ddl; |
| 41 | rel->flag = ddl_drop_seq; |
| 42 | rel->exps = exps; |
| 43 | rel->card = 0; |
| 44 | rel->nrcols = 0; |
| 45 | return rel; |
| 46 | } |
| 47 | |
| 48 | static sql_rel * |
| 49 | rel_seq(sql_allocator *sa, int cat_type, char *sname, sql_sequence *s, sql_rel *r, sql_exp *val) |
| 50 | { |
| 51 | sql_rel *rel = rel_create(sa); |
| 52 | list *exps = new_exp_list(sa); |
| 53 | if(!rel || !exps) |
| 54 | return NULL; |
| 55 | |
| 56 | if (val) |
| 57 | append(exps, val); |
| 58 | else |
| 59 | append(exps, exp_atom_int(sa, 0)); |
| 60 | append(exps, exp_atom_str(sa, sname, sql_bind_localtype("str" ) )); |
| 61 | append(exps, exp_atom_str(sa, s->base.name, sql_bind_localtype("str" ) )); |
| 62 | append(exps, exp_atom_ptr(sa, s)); |
| 63 | rel->l = r; |
| 64 | rel->r = NULL; |
| 65 | rel->op = op_ddl; |
| 66 | rel->flag = cat_type; |
| 67 | rel->exps = exps; |
| 68 | rel->card = CARD_MULTI; |
| 69 | rel->nrcols = 0; |
| 70 | return rel; |
| 71 | } |
| 72 | |
| 73 | static sql_rel * |
| 74 | rel_create_seq( |
| 75 | mvc *sql, |
| 76 | sql_schema *ss, |
| 77 | dlist *qname, |
| 78 | sql_subtype *tpe, |
| 79 | lng start, |
| 80 | lng inc, |
| 81 | lng min, |
| 82 | lng max, |
| 83 | lng cache, |
| 84 | bit cycle, |
| 85 | bit bedropped) |
| 86 | { |
| 87 | sql_rel *res = NULL; |
| 88 | sql_sequence *seq = NULL; |
| 89 | char *name = qname_table(qname); |
| 90 | char *sname = qname_schema(qname); |
| 91 | sql_schema *s = NULL; |
| 92 | |
| 93 | if (sname && !(s = mvc_bind_schema(sql, sname))) |
| 94 | return sql_error(sql, 02, SQLSTATE(3F000) "CREATE SEQUENCE: no such schema '%s'" , sname); |
| 95 | if (s == NULL) |
| 96 | s = ss; |
| 97 | (void) tpe; |
| 98 | if (find_sql_sequence(s, name)) { |
| 99 | return sql_error(sql, 02, SQLSTATE(42000) "CREATE SEQUENCE: name '%s' already in use" , name); |
| 100 | } else if (!mvc_schema_privs(sql, s)) { |
| 101 | return sql_error(sql, 02, SQLSTATE(42000) "CREATE SEQUENCE: insufficient privileges " |
| 102 | "for '%s' in schema '%s'" , stack_get_string(sql, "current_user" ), s->base.name); |
| 103 | } |
| 104 | |
| 105 | /* generate defaults */ |
| 106 | if (is_lng_nil(start)) start = 1; |
| 107 | if (is_lng_nil(inc)) inc = 1; |
| 108 | if (is_lng_nil(min)) min = 0; |
| 109 | if (cycle && (!is_lng_nil(max) && max < 0)) cycle = 0; |
| 110 | if (is_lng_nil(max)) max = 0; |
| 111 | if (is_lng_nil(cache)) cache = 1; |
| 112 | |
| 113 | seq = create_sql_sequence(sql->sa, s, name, start, min, max, inc, cache, cycle); |
| 114 | seq->bedropped = bedropped; |
| 115 | res = rel_seq(sql->sa, ddl_create_seq, s->base.name, seq, NULL, NULL); |
| 116 | /* for multi statements we keep the sequence around */ |
| 117 | if (res && stack_has_frame(sql, "MUL" ) != 0) { |
| 118 | if(!stack_push_rel_view(sql, name, rel_dup(res))) |
| 119 | return sql_error(sql, 02, SQLSTATE(HY001) MAL_MALLOC_FAIL); |
| 120 | } |
| 121 | |
| 122 | return res; |
| 123 | } |
| 124 | |
| 125 | #define SEQ_TYPE 0 |
| 126 | #define SEQ_START 1 |
| 127 | #define SEQ_INC 2 |
| 128 | #define SEQ_MIN 3 |
| 129 | #define SEQ_MAX 4 |
| 130 | #define SEQ_CYCLE 5 |
| 131 | #define SEQ_CACHE 6 |
| 132 | |
| 133 | static sql_rel * |
| 134 | list_create_seq( |
| 135 | mvc *sql, |
| 136 | sql_schema *ss, |
| 137 | dlist *qname, |
| 138 | dlist *options, |
| 139 | bit bedropped) |
| 140 | { |
| 141 | dnode *n; |
| 142 | sql_subtype *t = NULL; |
| 143 | lng start = lng_nil, inc = lng_nil, min = lng_nil, max = lng_nil, cache = lng_nil; |
| 144 | unsigned int used = 0; |
| 145 | bit cycle = 0; |
| 146 | |
| 147 | if (options) { |
| 148 | /* check if no option is given twice */ |
| 149 | for (n = options->h; n; n = n->next) { |
| 150 | symbol *s = n->data.sym; |
| 151 | |
| 152 | switch(s->token) { |
| 153 | case SQL_TYPE: { |
| 154 | bool found = false; |
| 155 | const char *valid_types[4] = {"tinyint" , "smallint" , "int" , "bigint" }; |
| 156 | size_t number_valid_types = sizeof(valid_types) / sizeof(valid_types[0]); |
| 157 | |
| 158 | if ((used&(1<<SEQ_TYPE))) |
| 159 | return sql_error(sql, 02, SQLSTATE(3F000) "CREATE SEQUENCE: AS type found should be used as most once" ); |
| 160 | used |= (1<<SEQ_TYPE); |
| 161 | t = &s->data.lval->h->data.typeval; |
| 162 | for (size_t i = 0; i < number_valid_types; i++) { |
| 163 | if (strcasecmp(valid_types[i], t->type->sqlname) == 0) { |
| 164 | found = true; |
| 165 | break; |
| 166 | } |
| 167 | } |
| 168 | if (!found) |
| 169 | return sql_error(sql, 02, SQLSTATE(42000) "CREATE SEQUENCE: The type of the sequence must be either tinyint, smallint, int or bigint" ); |
| 170 | } break; |
| 171 | case SQL_START: |
| 172 | if ((used&(1<<SEQ_START))) |
| 173 | return sql_error(sql, 02, SQLSTATE(3F000) "CREATE SEQUENCE: START value should be passed as most once" ); |
| 174 | used |= (1<<SEQ_START); |
| 175 | if (is_lng_nil(s->data.l_val)) |
| 176 | return sql_error(sql, 02, SQLSTATE(42000) "CREATE SEQUENCE: START must not be null" ); |
| 177 | start = s->data.l_val; |
| 178 | break; |
| 179 | case SQL_INC: |
| 180 | if ((used&(1<<SEQ_INC))) |
| 181 | return sql_error(sql, 02, SQLSTATE(3F000) "CREATE SEQUENCE: INCREMENT value should be passed as most once" ); |
| 182 | used |= (1<<SEQ_INC); |
| 183 | if (is_lng_nil(s->data.l_val)) |
| 184 | return sql_error(sql, 02, SQLSTATE(42000) "CREATE SEQUENCE: INCREMENT must not be null" ); |
| 185 | inc = s->data.l_val; |
| 186 | break; |
| 187 | case SQL_MINVALUE: |
| 188 | if ((used&(1<<SEQ_MIN))) |
| 189 | return sql_error(sql, 02, SQLSTATE(3F000) "CREATE SEQUENCE: MINVALUE or NO MINVALUE should be passed as most once" ); |
| 190 | used |= (1<<SEQ_MIN); |
| 191 | if (is_lng_nil(s->data.l_val)) |
| 192 | return sql_error(sql, 02, SQLSTATE(42000) "CREATE SEQUENCE: MINVALUE must not be null" ); |
| 193 | min = s->data.l_val; |
| 194 | break; |
| 195 | case SQL_MAXVALUE: |
| 196 | if ((used&(1<<SEQ_MAX))) |
| 197 | return sql_error(sql, 02, SQLSTATE(3F000) "CREATE SEQUENCE: MAXVALUE or NO MAXVALUE should be passed as most once" ); |
| 198 | used |= (1<<SEQ_MAX); |
| 199 | if (is_lng_nil(s->data.l_val)) |
| 200 | return sql_error(sql, 02, SQLSTATE(42000) "CREATE SEQUENCE: MAXVALUE must be non-NULL" ); |
| 201 | max = s->data.l_val; |
| 202 | break; |
| 203 | case SQL_CYCLE: |
| 204 | if ((used&(1<<SEQ_CYCLE))) |
| 205 | return sql_error(sql, 02, SQLSTATE(3F000) "CREATE SEQUENCE: CYCLE or NO CYCLE should be passed as most once" ); |
| 206 | used |= (1<<SEQ_CYCLE); |
| 207 | cycle = s->data.i_val != 0; |
| 208 | break; |
| 209 | case SQL_CACHE: |
| 210 | if ((used&(1<<SEQ_CACHE))) |
| 211 | return sql_error(sql, 02, SQLSTATE(3F000) "CREATE SEQUENCE: CACHE value should be passed as most once" ); |
| 212 | used |= (1<<SEQ_CACHE); |
| 213 | if (is_lng_nil(s->data.l_val)) |
| 214 | return sql_error(sql, 02, SQLSTATE(42000) "CREATE SEQUENCE: CACHE must be non-NULL" ); |
| 215 | cache = s->data.l_val; |
| 216 | break; |
| 217 | default: |
| 218 | assert(0); |
| 219 | } |
| 220 | } |
| 221 | if (!is_lng_nil(start)) { |
| 222 | if (!is_lng_nil(min) && start < min) |
| 223 | return sql_error(sql, 02, SQLSTATE(42000) "CREATE SEQUENCE: START value is lesser than MINVALUE (" LLFMT" < " LLFMT")" , start, min); |
| 224 | if (!is_lng_nil(max) && start > max) |
| 225 | return sql_error(sql, 02, SQLSTATE(42000) "CREATE SEQUENCE: START value is higher than MAXVALUE (" LLFMT" > " LLFMT")" , start, max); |
| 226 | } |
| 227 | if (!is_lng_nil(min) && !is_lng_nil(max) && max < min) |
| 228 | return sql_error(sql, 02, SQLSTATE(42000) "CREATE SEQUENCE: MAXVALUE value is lesser than MINVALUE (" LLFMT" < " LLFMT")" , max, min); |
| 229 | } |
| 230 | if (is_lng_nil(start) && !is_lng_nil(min) && min) /* if start value not set, set it to the minimum if available */ |
| 231 | start = min; |
| 232 | return rel_create_seq(sql, ss, qname, t, start, inc, min, max, cache, cycle, bedropped); |
| 233 | } |
| 234 | |
| 235 | static sql_rel * |
| 236 | rel_alter_seq( |
| 237 | sql_query *query, |
| 238 | sql_schema *ss, |
| 239 | dlist *qname, |
| 240 | sql_subtype *tpe, |
| 241 | dlist* start_list, |
| 242 | lng inc, |
| 243 | lng min, |
| 244 | lng max, |
| 245 | lng cache, |
| 246 | bit cycle) |
| 247 | { |
| 248 | mvc *sql = query->sql; |
| 249 | char* name = qname_table(qname); |
| 250 | char *sname = qname_schema(qname); |
| 251 | sql_sequence *seq; |
| 252 | sql_schema *s = NULL; |
| 253 | |
| 254 | int start_type = start_list->h->data.i_val; |
| 255 | sql_rel *r = NULL; |
| 256 | sql_exp *val = NULL; |
| 257 | |
| 258 | assert(start_list->h->type == type_int); |
| 259 | if (sname && !(s = mvc_bind_schema(sql, sname))) |
| 260 | return sql_error(sql, 02, SQLSTATE(3F000) "ALTER SEQUENCE: no such schema '%s'" , sname); |
| 261 | if (!s) |
| 262 | s = ss; |
| 263 | (void) tpe; |
| 264 | if (!(seq = find_sql_sequence(s, name))) { |
| 265 | return sql_error(sql, 02, SQLSTATE(42000) "ALTER SEQUENCE: no such sequence '%s'" , name); |
| 266 | } |
| 267 | if (!mvc_schema_privs(sql, s)) { |
| 268 | return sql_error(sql, 02, SQLSTATE(42000) "ALTER SEQUENCE: insufficient privileges " |
| 269 | "for '%s' in schema '%s'" , stack_get_string(sql, "current_user" ), s->base.name); |
| 270 | } |
| 271 | |
| 272 | /* first alter the known values */ |
| 273 | seq = create_sql_sequence(sql->sa, s, name, seq->start, min, max, inc, cache, (bit) cycle); |
| 274 | |
| 275 | /* restart may be a query, i.e. we create a statement |
| 276 | restart(ssname,seqname,value) */ |
| 277 | |
| 278 | if (start_type == 0) { |
| 279 | val = exp_atom_lng(sql->sa, seq->start); |
| 280 | } else if (start_type == 1) { /* value (exp) */ |
| 281 | exp_kind ek = {type_value, card_value, FALSE}; |
| 282 | int is_last = 0; |
| 283 | sql_subtype *lng_t = sql_bind_localtype("lng" ); |
| 284 | |
| 285 | val = rel_value_exp2(query, &r, start_list->h->next->data.sym, sql_sel, ek, &is_last); |
| 286 | if (!val || !(val = rel_check_type(sql, lng_t, r, val, type_equal))) |
| 287 | return NULL; |
| 288 | if (r && r->op == op_project) { |
| 289 | exp_label(sql->sa, val, ++sql->label); |
| 290 | val = rel_project_add_exp(sql, r, val); |
| 291 | } |
| 292 | } else if (start_type == 2) { |
| 293 | assert (start_list->h->next->type == type_lng); |
| 294 | val = exp_atom_lng(sql->sa, start_list->h->next->data.l_val); |
| 295 | } |
| 296 | if (val && val->card > CARD_ATOM) { |
| 297 | sql_subaggr *zero_or_one = sql_bind_aggr(sql->sa, sql->session->schema, "zero_or_one" , exp_subtype(val)); |
| 298 | val = exp_aggr1(sql->sa, val, zero_or_one, 0, 0, CARD_ATOM, 0); |
| 299 | } |
| 300 | return rel_seq(sql->sa, ddl_alter_seq, s->base.name, seq, r, val); |
| 301 | } |
| 302 | |
| 303 | static sql_rel * |
| 304 | list_alter_seq( |
| 305 | sql_query *query, |
| 306 | sql_schema *ss, |
| 307 | dlist *qname, |
| 308 | dlist *options) |
| 309 | { |
| 310 | mvc *sql = query->sql; |
| 311 | dnode *n; |
| 312 | sql_subtype* t = NULL; |
| 313 | lng inc = lng_nil, min = lng_nil, max = lng_nil, cache = lng_nil; |
| 314 | dlist *start = NULL; |
| 315 | unsigned int used = 0; |
| 316 | bit cycle = 0; |
| 317 | |
| 318 | /* check if no option is given twice */ |
| 319 | for (n = options->h; n; n = n->next) { |
| 320 | symbol *s = n->data.sym; |
| 321 | |
| 322 | switch(s->token) { |
| 323 | case SQL_TYPE: |
| 324 | if ((used&(1<<SEQ_TYPE))) |
| 325 | return sql_error(sql, 02, SQLSTATE(3F000) "ALTER SEQUENCE: AS type found should be used as most once" ); |
| 326 | used |= (1<<SEQ_TYPE); |
| 327 | t = &s->data.lval->h->data.typeval; |
| 328 | break; |
| 329 | case SQL_START: |
| 330 | if ((used&(1<<SEQ_START))) |
| 331 | return sql_error(sql, 02, SQLSTATE(3F000) "ALTER SEQUENCE: START value should be passed as most once" ); |
| 332 | used |= (1<<SEQ_START); |
| 333 | if (is_lng_nil(s->data.l_val)) |
| 334 | return sql_error(sql, 02, SQLSTATE(42000) "ALTER SEQUENCE: START must be non-NULL" ); |
| 335 | start = s->data.lval; |
| 336 | break; |
| 337 | case SQL_INC: |
| 338 | if ((used&(1<<SEQ_INC))) |
| 339 | return sql_error(sql, 02, SQLSTATE(3F000) "ALTER SEQUENCE: INCREMENT value should be passed as most once" ); |
| 340 | used |= (1<<SEQ_INC); |
| 341 | if (is_lng_nil(s->data.l_val)) |
| 342 | return sql_error(sql, 02, SQLSTATE(42000) "ALTER SEQUENCE: INCREMENT must be non-NULL" ); |
| 343 | inc = s->data.l_val; |
| 344 | break; |
| 345 | case SQL_MINVALUE: |
| 346 | if ((used&(1<<SEQ_MIN))) |
| 347 | return sql_error(sql, 02, SQLSTATE(3F000) "ALTER SEQUENCE: MINVALUE or NO MINVALUE should be passed as most once" ); |
| 348 | used |= (1<<SEQ_MIN); |
| 349 | if (is_lng_nil(s->data.l_val)) |
| 350 | return sql_error(sql, 02, SQLSTATE(42000) "ALTER SEQUENCE: MINVALUE must be non-NULL" ); |
| 351 | min = s->data.l_val; |
| 352 | break; |
| 353 | case SQL_MAXVALUE: |
| 354 | if ((used&(1<<SEQ_MAX))) |
| 355 | return sql_error(sql, 02, SQLSTATE(3F000) "ALTER SEQUENCE: MAXVALUE or NO MAXVALUE should be passed as most once" ); |
| 356 | used |= (1<<SEQ_MAX); |
| 357 | if (is_lng_nil(s->data.l_val)) |
| 358 | return sql_error(sql, 02, SQLSTATE(42000) "ALTER SEQUENCE: MAXVALUE must be non-NULL" ); |
| 359 | max = s->data.l_val; |
| 360 | break; |
| 361 | case SQL_CYCLE: |
| 362 | if ((used&(1<<SEQ_CYCLE))) |
| 363 | return sql_error(sql, 02, SQLSTATE(3F000) "ALTER SEQUENCE: CYCLE or NO CYCLE should be passed as most once" ); |
| 364 | used |= (1<<SEQ_CYCLE); |
| 365 | cycle = s->data.i_val != 0; |
| 366 | break; |
| 367 | case SQL_CACHE: |
| 368 | if ((used&(1<<SEQ_CACHE))) |
| 369 | return sql_error(sql, 02, SQLSTATE(3F000) "ALTER SEQUENCE: CACHE value should be passed as most once" ); |
| 370 | used |= (1<<SEQ_CACHE); |
| 371 | if (is_lng_nil(s->data.l_val)) |
| 372 | return sql_error(sql, 02, SQLSTATE(42000) "ALTER SEQUENCE: CACHE must be non-NULL" ); |
| 373 | cache = s->data.l_val; |
| 374 | break; |
| 375 | default: |
| 376 | assert(0); |
| 377 | } |
| 378 | } |
| 379 | if (!is_lng_nil(min) && !is_lng_nil(max) && max < min) |
| 380 | return sql_error(sql, 02, SQLSTATE(42000) "ALTER SEQUENCE: MAXVALUE value is lesser than MINVALUE (" LLFMT" < " LLFMT")" , max, min); |
| 381 | return rel_alter_seq(query, ss, qname, t, start, inc, min, max, cache, cycle); |
| 382 | } |
| 383 | |
| 384 | sql_rel * |
| 385 | rel_sequences(sql_query *query, symbol *s) |
| 386 | { |
| 387 | mvc *sql = query->sql; |
| 388 | sql_rel *res = NULL; |
| 389 | |
| 390 | switch (s->token) { |
| 391 | case SQL_CREATE_SEQ: |
| 392 | { |
| 393 | dlist *l = s->data.lval; |
| 394 | |
| 395 | res = list_create_seq( |
| 396 | /* mvc* sql */ sql, |
| 397 | /* sql_schema* s */ cur_schema(sql), |
| 398 | /* dlist* qname */ l->h->data.lval, |
| 399 | /* dlist* options */ l->h->next->data.lval, |
| 400 | /* bit bedropped */ (bit) (l->h->next->next->data.i_val != 0)); |
| 401 | } |
| 402 | break; |
| 403 | case SQL_ALTER_SEQ: |
| 404 | { |
| 405 | dlist* l = s->data.lval; |
| 406 | |
| 407 | res = list_alter_seq( |
| 408 | /* mvc* sql */ query, |
| 409 | /* sql_schema* s */ cur_schema(sql), |
| 410 | /* dlist* qname */ l->h->data.lval, |
| 411 | /* dlist* options */ l->h->next->data.lval); |
| 412 | } |
| 413 | break; |
| 414 | case SQL_DROP_SEQ: |
| 415 | { |
| 416 | dlist *l = s->data.lval; |
| 417 | char *sname = qname_schema(l->h->data.lval); |
| 418 | char *seqname = qname_table(l->h->data.lval); |
| 419 | |
| 420 | if (!sname) { |
| 421 | sql_schema *ss = cur_schema(sql); |
| 422 | |
| 423 | sname = ss->base.name; |
| 424 | } |
| 425 | res = rel_drop_seq(sql->sa, sname, seqname); |
| 426 | } |
| 427 | break; |
| 428 | default: |
| 429 | return sql_error(sql, 01, SQLSTATE(42000) "sql_stmt Symbol(%p)->token = %s" , s, token2string(s->token)); |
| 430 | } |
| 431 | sql->type = Q_SCHEMA; |
| 432 | return res; |
| 433 | } |
| 434 | |