* This file is part of the oGRAC project.
* Copyright (c) 2024 Huawei Technologies Co.,Ltd.
*
* oGRAC is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
*
* ddl_constraint_parser.c
*
*
* IDENTIFICATION
* src/ogsql/parser_ddl/ddl_constraint_parser.c
*
* -------------------------------------------------------------------------
*/
#include "ddl_constraint_parser.h"
#include "srv_instance.h"
#include "ogsql_serial.h"
#include "ddl_index_parser.h"
#include "ddl_column_parser.h"
#include "ddl_parser_common.h"
#include "cond_parser.h"
#include "ddl_index_parser.h"
* a recursive process which will stop when encountering the first non-constraint-state word.
* the so-called constraint-state word(s) are:
* "USING INDEX", "ENABLE", "DISABLE", "DEFERRABLE", "NOT DEFERRABLE", "INITIALLY IMMEDIATE", "INITIALLY DEFERRED",
* "RELY", "NORELY", "VALIDATE", "NOVALIDATE"
* use a recursive calling because multiple constraint-state word(s) can be specified in one constraint_state clause.
*/
static status_t sql_parse_constraint_state(sql_stmt_t *stmt, lex_t *lex, knl_constraint_def_t *cons_def, word_t
*next_word)
{
knl_constraint_state_t *cons_state = NULL;
word_t word;
uint32 hit_index = OG_INVALID_ID32;
CM_POINTER3(stmt, lex, cons_def);
cons_state = &cons_def->cons_state;
OG_RETURN_IFERR(lex_fetch(lex, &word));
switch (word.id) {
case KEY_WORD_USING:
OG_RETURN_IFERR(lex_expected_fetch_word(lex, "index"));
OG_RETURN_IFERR(sql_parse_using_index(stmt, lex, cons_def));
if ((cons_def->type != CONS_TYPE_PRIMARY) && (cons_def->type != CONS_TYPE_UNIQUE)) {
OG_SRC_THROW_ERROR_EX(word.loc, ERR_SQL_SYNTAX_ERROR,
"\"USING INDEX\" cannot be specified in a constraint clause other than primary "
"key constraint and unique constraint.");
return OG_ERROR;
}
cons_state->is_use_index = OG_TRUE;
break;
case KEY_WORD_ENABLE:
cons_state->is_enable = OG_TRUE;
break;
case KEY_WORD_DISABLE:
cons_state->is_enable = OG_FALSE;
break;
case KEY_WORD_NOT:
OG_RETURN_IFERR(lex_expected_fetch_word(lex, "deferrable"));
cons_state->deferrable_ops = STATE_NOT_DEFERRABLE;
break;
case KEY_WORD_DEFERRABLE:
cons_state->deferrable_ops = STATE_DEFERRABLE;
break;
case KEY_WORD_INITIALLY:
OG_RETURN_IFERR(lex_expected_fetch_1of2(lex, "immediate", "deferred", &hit_index));
if (hit_index == 0) {
cons_state->initially_ops = STATE_INITIALLY_IMMEDIATE;
} else {
cons_state->initially_ops = STATE_INITIALLY_DEFERRED;
}
break;
case KEY_WORD_RELY:
cons_state->rely_ops = STATE_RELY;
break;
case KEY_WORD_NO_RELY:
cons_state->rely_ops = STATE_NO_RELY;
break;
case KEY_WORD_VALIDATE:
cons_state->is_validate = OG_TRUE;
break;
case KEY_WORD_NO_VALIDATE:
cons_state->is_validate = OG_FALSE;
break;
case KEY_WORD_PARALLEL:
if ((cons_def->type != CONS_TYPE_PRIMARY) && (cons_def->type != CONS_TYPE_UNIQUE)) {
OG_SRC_THROW_ERROR_EX(word.loc, ERR_SQL_SYNTAX_ERROR,
"\"PARALLEL\" cannot be specified in a constraint clause other than primary "
"key constraint and unique constraint.");
return OG_ERROR;
}
OG_RETURN_IFERR(sql_parse_parallelism(lex, &word, &cons_def->index.parallelism, OG_MAX_INDEX_PARALLELISM));
break;
case KEY_WORD_REVERSE:
if ((cons_def->type != CONS_TYPE_PRIMARY) && (cons_def->type != CONS_TYPE_UNIQUE)) {
OG_SRC_THROW_ERROR_EX(word.loc, ERR_SQL_SYNTAX_ERROR,
"\"REVERSE\" cannot be specified in a constraint clause other than primary "
"key constraint and unique constraint.");
return OG_ERROR;
}
OG_RETURN_IFERR(sql_parse_reverse(&word, &cons_def->index.is_reverse));
break;
default:
*next_word = word;
return OG_SUCCESS;
}
OG_RETURN_IFERR(sql_parse_constraint_state(stmt, lex, cons_def, &word));
*next_word = word;
return OG_SUCCESS;
}
status_t sql_parse_foreign_key(sql_stmt_t *stmt, lex_t *lex, knl_constraint_def_t *cons_def)
{
knl_reference_def_t *ref = NULL;
cons_def->type = CONS_TYPE_REFERENCE;
if (lex_expected_fetch_word(lex, "KEY") != OG_SUCCESS) {
return OG_ERROR;
}
ref = &cons_def->ref;
if (sql_parse_column_list(stmt, lex, &cons_def->columns, OG_FALSE, NULL) != OG_SUCCESS) {
return OG_ERROR;
}
if (lex_expected_fetch_word(lex, "REFERENCES") != OG_SUCCESS) {
return OG_ERROR;
}
if (sql_parse_references_clause(stmt, lex, &ref->ref_user, &ref->ref_table, &ref->refactor, &ref->ref_columns) !=
OG_SUCCESS) {
return OG_ERROR;
}
if (cons_def->columns.count != ref->ref_columns.count) {
OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "number of referencing columns must match referenced columns.");
return OG_ERROR;
}
return OG_SUCCESS;
}
status_t sql_parse_primary_unique_cons(sql_stmt_t *stmt, lex_t *lex, constraint_type_t type,
knl_constraint_def_t *cons_def)
{
word_t word;
cons_def->type = type;
cons_def->index.cr_mode = OG_INVALID_ID8;
cons_def->index.pctfree = OG_INVALID_ID32;
if (type == CONS_TYPE_PRIMARY) {
if (lex_expected_fetch_word(lex, "KEY") != OG_SUCCESS) {
return OG_ERROR;
}
}
if (sql_parse_column_list(stmt, lex, &cons_def->columns, OG_FALSE, NULL) != OG_SUCCESS) {
return OG_ERROR;
}
OG_RETURN_IFERR(sql_parse_constraint_state(stmt, lex, cons_def, &word));
if (word.type == WORD_TYPE_EOF || IS_SPEC_CHAR(&word, ',')) {
if (!IS_USEINDEX_FLAG_SPECIFIED(cons_def)) {
if (type == CONS_TYPE_PRIMARY) {
cons_def->index.primary = OG_TRUE;
} else {
cons_def->index.unique = OG_TRUE;
}
}
lex_back(lex, &word);
return OG_SUCCESS;
}
OG_SRC_THROW_ERROR_EX(word.text.loc, ERR_SQL_SYNTAX_ERROR, "unexpected text %s", W2S(&word));
return OG_ERROR;
}
static status_t sql_fetch_column_in_expr(visit_assist_t *va, expr_node_t **node)
{
if ((*node)->type != EXPR_NODE_COLUMN && (*node)->type != EXPR_NODE_DIRECT_COLUMN) {
return OG_SUCCESS;
}
sql_walker_t *walker = (sql_walker_t *)va->param0;
text_t *new_col = NULL;
text_t *col = NULL;
for (uint32 i = 0; i < walker->columns->count; i++) {
col = (text_t *)cm_galist_get(walker->columns, i);
if (!cm_compare_text(col, &(*node)->word.column.name.value)) {
return OG_SUCCESS;
}
}
OG_RETURN_IFERR(cm_galist_new(walker->columns, sizeof(text_t), (void **)&new_col));
*new_col = (*node)->word.column.name.value;
return OG_SUCCESS;
}
static status_t sql_verify_outline_check(sql_stmt_t *stmt, knl_table_def_t *verf_data, knl_constraint_def_t *cons_def,
cond_tree_t *cond)
{
text_t save_check_text;
knl_check_def_t *check = &cons_def->check;
sql_verifier_t verf = { 0 };
sql_walker_t walker = { 0 };
verf.context = stmt->context;
verf.stmt = stmt;
verf.is_check_cons = OG_TRUE;
verf.table_def = verf_data;
verf.create_table_define = OG_TRUE;
verf.excl_flags = SQL_EXCL_AGGR | SQL_EXCL_STAR | SQL_EXCL_PRIOR | SQL_EXCL_SUBSELECT | SQL_EXCL_JOIN |
SQL_EXCL_BIND_PARAM | SQL_EXCL_ROWNUM | SQL_EXCL_ROWID | SQL_EXCL_LOB_COL | SQL_EXCL_SEQUENCE | SQL_EXCL_CASE |
SQL_EXCL_ROWSCN | SQL_EXCL_WIN_SORT | SQL_EXCL_ROWNODEID;
if (sql_verify_cond(&verf, cond) != OG_SUCCESS) {
return OG_ERROR;
}
if (check->text.len > OG_MAX_CHECK_VALUE_LEN) {
OG_SRC_THROW_ERROR_EX(cond->loc, ERR_SQL_SYNTAX_ERROR, "length of CHECK's value exceed maximum: %d",
OG_MAX_CHECK_VALUE_LEN);
return OG_ERROR;
}
cm_galist_init(&cons_def->columns, stmt->context, sql_alloc_mem);
walker.context = stmt->context;
walker.stmt = stmt;
walker.columns = &cons_def->columns;
visit_assist_t v_ast = { 0 };
sql_init_visit_assist(&v_ast, stmt, NULL);
v_ast.param0 = (void*) &walker;
OG_RETURN_IFERR(visit_cond_node(&v_ast, cond->root, sql_fetch_column_in_expr));
save_check_text = check->text;
return sql_copy_text(stmt->context, &save_check_text, &check->text);
}
static status_t sql_verify_inline_check(sql_stmt_t *stmt, knl_column_def_t *def, cond_tree_t *cond)
{
text_t save_check_text;
sql_verifier_t verf = { 0 };
verf.context = stmt->context;
verf.stmt = stmt;
verf.table_def = (knl_table_def_t *)def->table;
verf.column = def;
verf.is_check_cons = OG_TRUE;
verf.create_table_define = OG_TRUE;
verf.excl_flags = SQL_EXCL_AGGR | SQL_EXCL_STAR | SQL_EXCL_PRIOR | SQL_EXCL_SUBSELECT | SQL_EXCL_JOIN |
SQL_EXCL_BIND_PARAM | SQL_EXCL_ROWNUM | SQL_EXCL_ROWID | SQL_EXCL_LOB_COL | SQL_EXCL_SEQUENCE | SQL_EXCL_CASE |
SQL_EXCL_ROWSCN | SQL_EXCL_WIN_SORT | SQL_EXCL_ROWNODEID;
if (sql_verify_cond(&verf, cond) != OG_SUCCESS) {
return OG_ERROR;
}
if (def->check_text.len > OG_MAX_CHECK_VALUE_LEN) {
OG_SRC_THROW_ERROR_EX(cond->loc, ERR_SQL_SYNTAX_ERROR, "length of CHECK's value exceed maximum: %d",
OG_MAX_CHECK_VALUE_LEN);
return OG_ERROR;
}
save_check_text = def->check_text;
return sql_copy_text(stmt->context, &save_check_text, &def->check_text);
}
status_t sql_parse_add_check(sql_stmt_t *stmt, lex_t *lex, knl_altable_def_t *def, knl_constraint_def_t *cons_def)
{
word_t word;
status_t status;
cond_tree_t *cond = NULL;
cons_def->type = CONS_TYPE_CHECK;
status = lex_expected_fetch_bracket(lex, &word);
OG_RETURN_IFERR(status);
OG_RETURN_IFERR(lex_push(lex, &word.text));
cons_def->check.text = word.text.value;
cm_trim_text(&cons_def->check.text);
lex->flags = LEX_WITH_ARG | LEX_WITH_OWNER;
stmt->is_check = OG_TRUE;
status = sql_create_cond_until(stmt, &cond, &word);
stmt->is_check = OG_FALSE;
lex_pop(lex);
OG_RETURN_IFERR(status);
if (word.type != WORD_TYPE_EOF) {
OG_SRC_THROW_ERROR_EX(word.loc, ERR_SQL_SYNTAX_ERROR, "expected end but \"%s\" found", W2S(&word));
return OG_ERROR;
}
cons_def->check.cond = cond;
return sql_verify_outline_check(stmt, NULL, cons_def, cond);
}
status_t sql_parse_constraint(sql_stmt_t *stmt, lex_t *lex, knl_altable_def_t *def)
{
word_t word;
status_t status;
uint32 save_flags;
knl_constraint_def_t *cons_def = &def->cons_def.new_cons;
if (def->action == ALTABLE_ADD_CONSTRAINT) {
OG_RETURN_IFERR(sql_try_parse_if_not_exists(lex, &def->options));
}
save_flags = lex->flags;
lex->flags = LEX_SINGLE_WORD;
status = lex_expected_fetch_variant(lex, &word);
lex->flags = save_flags;
OG_RETURN_IFERR(status);
OG_RETURN_IFERR(sql_copy_object_name(stmt->context, word.type, (text_t *)&word.text, &cons_def->name));
cons_def->cons_state.is_anonymous = OG_FALSE;
OG_RETURN_IFERR(lex_expected_fetch(lex, &word));
switch ((key_wid_t)word.id) {
case KEY_WORD_PRIMARY:
status = sql_parse_primary_unique_cons(stmt, lex, CONS_TYPE_PRIMARY, cons_def);
break;
case KEY_WORD_FOREIGN:
status = sql_parse_foreign_key(stmt, lex, cons_def);
break;
case KEY_WORD_CHECK:
status = sql_parse_add_check(stmt, lex, def, cons_def);
break;
case KEY_WORD_UNIQUE:
status = sql_parse_primary_unique_cons(stmt, lex, CONS_TYPE_UNIQUE, cons_def);
break;
default:
OG_SRC_THROW_ERROR_EX(word.text.loc, ERR_SQL_SYNTAX_ERROR, ", expected but %s found", W2S(&word));
status = OG_ERROR;
break;
}
return status;
}
static status_t sql_append_inline_checks_column(sql_stmt_t *stmt, knl_constraint_def_t *cons_def,
const knl_column_def_t *column)
{
text_t *col = NULL;
if (!CM_IS_EMPTY(&column->inl_chk_cons_name)) {
cons_def->cons_state.is_anonymous = OG_FALSE;
cons_def->name = column->inl_chk_cons_name;
}
cm_galist_init(&cons_def->columns, stmt->context, sql_alloc_mem);
if (cm_galist_new(&cons_def->columns, sizeof(text_t), (pointer_t *)&col) != OG_SUCCESS) {
return OG_ERROR;
}
*col = column->name;
cons_def->check.text = column->check_text;
cons_def->check.cond = column->check_cond;
return OG_SUCCESS;
}
static status_t sql_alloc_inline_cons(sql_stmt_t *stmt, constraint_type_t type, galist_t *constraints,
knl_constraint_def_t **cons_def)
{
if (cm_galist_new(constraints, sizeof(knl_constraint_def_t), (pointer_t *)cons_def) != OG_SUCCESS) {
return OG_ERROR;
}
(*cons_def)->type = type;
if (sql_alloc_mem(stmt->context, OG_NAME_BUFFER_SIZE, (pointer_t *)&(*cons_def)->name.str) != OG_SUCCESS) {
return OG_ERROR;
}
knl_get_system_name(&stmt->session->knl_session, type, (*cons_def)->name.str, OG_NAME_BUFFER_SIZE);
(*cons_def)->name.len = (uint32)strlen((*cons_def)->name.str);
(*cons_def)->cons_state.is_anonymous = OG_TRUE;
(*cons_def)->cons_state.is_enable = OG_TRUE;
(*cons_def)->cons_state.is_validate = OG_TRUE;
(*cons_def)->cons_state.is_cascade = OG_TRUE;
return OG_SUCCESS;
}
static status_t sql_create_inline_checks(sql_stmt_t *stmt, knl_table_def_t *def)
{
uint32 i;
knl_column_def_t *column = NULL;
knl_constraint_def_t *cons_def = NULL;
for (i = 0; i < def->columns.count; i++) {
column = cm_galist_get(&def->columns, i);
if (!column->is_check) {
continue;
}
if (sql_alloc_inline_cons(stmt, CONS_TYPE_CHECK, &def->constraints, &cons_def) != OG_SUCCESS) {
return OG_ERROR;
}
if (sql_append_inline_checks_column(stmt, cons_def, column) != OG_SUCCESS) {
return OG_ERROR;
}
}
return OG_SUCCESS;
}
static status_t sql_append_inline_ref_column(sql_stmt_t *stmt, knl_table_def_t *def, const knl_column_def_t *column)
{
knl_constraint_def_t *cons_def = NULL;
text_t *name = NULL;
if (sql_alloc_inline_cons(stmt, CONS_TYPE_REFERENCE, &def->constraints, &cons_def) != OG_SUCCESS) {
return OG_ERROR;
}
if (!CM_IS_EMPTY(&column->inl_ref_cons_name)) {
cons_def->name = column->inl_ref_cons_name;
cons_def->cons_state.is_anonymous = OG_FALSE;
}
cm_galist_init(&cons_def->columns, stmt->context, sql_alloc_mem);
cons_def->ref.ref_user = column->ref_user;
cons_def->ref.ref_table = column->ref_table;
if (cm_galist_new(&cons_def->columns, sizeof(text_t), (pointer_t *)&name) != OG_SUCCESS) {
return OG_ERROR;
}
*name = column->name;
cons_def->ref.ref_columns = column->ref_columns;
cons_def->ref.refactor = column->refactor;
return OG_SUCCESS;
}
static status_t sql_create_inline_refs(sql_stmt_t *stmt, knl_table_def_t *def)
{
uint32 i;
knl_column_def_t *column = NULL;
for (i = 0; i < def->columns.count; i++) {
column = cm_galist_get(&def->columns, i);
if (!column->is_ref) {
continue;
}
if (column->typmod.is_array == OG_TRUE) {
OG_THROW_ERROR(ERR_REF_ON_ARRAY_COLUMN);
return OG_ERROR;
}
if (sql_append_inline_ref_column(stmt, def, column) != OG_SUCCESS) {
return OG_ERROR;
}
}
return OG_SUCCESS;
}
static status_t sql_create_inline_cons_index(sql_stmt_t *stmt, knl_table_def_t *def)
{
uint32 i;
knl_column_def_t *column = NULL;
knl_constraint_def_t *cons_def = NULL;
knl_index_col_def_t *index_column = NULL;
constraint_type_t type;
for (i = 0; i < def->columns.count; i++) {
column = cm_galist_get(&def->columns, i);
if ((!column->primary) && (!column->unique)) {
continue;
}
if (column->typmod.is_array == OG_TRUE) {
OG_THROW_ERROR(ERR_INDEX_ON_ARRAY_FIELD, T2S(&column->name));
return OG_ERROR;
}
type = column->primary ? CONS_TYPE_PRIMARY : CONS_TYPE_UNIQUE;
if (sql_alloc_inline_cons(stmt, type, &def->constraints, &cons_def) != OG_SUCCESS) {
return OG_ERROR;
}
if (type == CONS_TYPE_PRIMARY) {
if (!CM_IS_EMPTY(&column->inl_pri_cons_name)) {
cons_def->name = column->inl_pri_cons_name;
cons_def->cons_state.is_anonymous = OG_FALSE;
}
} else {
if (!CM_IS_EMPTY(&column->inl_uq_cons_name)) {
cons_def->name = column->inl_uq_cons_name;
cons_def->cons_state.is_anonymous = OG_FALSE;
}
}
cm_galist_init(&cons_def->columns, stmt->context, sql_alloc_mem);
if (cm_galist_new(&cons_def->columns, sizeof(knl_index_col_def_t), (pointer_t *)&index_column) != OG_SUCCESS) {
return OG_ERROR;
}
index_column->name = column->name;
index_column->mode = SORT_MODE_ASC;
cons_def->index.primary = (type == CONS_TYPE_PRIMARY);
cons_def->index.unique = (type == CONS_TYPE_UNIQUE);
cons_def->index.cr_mode = OG_INVALID_ID8;
cons_def->index.pctfree = OG_INVALID_ID32;
}
return OG_SUCCESS;
}
status_t sql_create_inline_cons(sql_stmt_t *stmt, knl_table_def_t *def)
{
if (def->pk_inline || def->uq_inline) {
if (sql_create_inline_cons_index(stmt, def) != OG_SUCCESS) {
return OG_ERROR;
}
}
if (def->rf_inline) {
if (sql_create_inline_refs(stmt, def) != OG_SUCCESS) {
return OG_ERROR;
}
}
if (def->chk_inline) {
if (sql_create_inline_checks(stmt, def) != OG_SUCCESS) {
return OG_ERROR;
}
}
return OG_SUCCESS;
}
static status_t sql_parse_check_outline_cons(sql_stmt_t *stmt, lex_t *lex, knl_table_def_t *def,
knl_constraint_def_t *cons_def)
{
status_t status;
word_t word;
cond_tree_t *cond = NULL;
if (cons_def == NULL) {
status = sql_alloc_inline_cons(stmt, CONS_TYPE_CHECK, &def->constraints, &cons_def);
OG_RETURN_IFERR(status);
}
cons_def->type = CONS_TYPE_CHECK;
status = lex_expected_fetch_bracket(lex, &word);
OG_RETURN_IFERR(status);
OG_RETURN_IFERR(lex_push(lex, &word.text));
cons_def->check.text = lex->curr_text->value;
cm_trim_text(&cons_def->check.text);
lex->flags = LEX_WITH_ARG | LEX_WITH_OWNER;
stmt->is_check = OG_TRUE;
status = sql_create_cond_until(stmt, &cond, &word);
stmt->is_check = OG_FALSE;
lex_pop(lex);
OG_RETURN_IFERR(status);
lex->flags = LEX_SINGLE_WORD;
if (word.type != WORD_TYPE_EOF) {
return OG_ERROR;
}
cons_def->check.cond = cond;
return OG_SUCCESS;
}
static status_t sql_try_parse_constraint(sql_stmt_t *stmt, lex_t *lex, knl_table_def_t *def, bool32 *result)
{
word_t word;
status_t status;
knl_constraint_def_t *cons_def = NULL;
status = lex_try_fetch_variant_excl(lex, &word, WORD_TYPE_DATATYPE, result);
OG_RETURN_IFERR(status);
if (*result == OG_FALSE) {
return OG_SUCCESS;
}
status = cm_galist_new(&def->constraints, sizeof(knl_constraint_def_t), (pointer_t *)&cons_def);
OG_RETURN_IFERR(status);
cons_def->cons_state.is_enable = OG_TRUE;
cons_def->cons_state.is_validate = OG_TRUE;
cons_def->cons_state.is_cascade = OG_TRUE;
status = sql_copy_object_name(stmt->context, word.type, (text_t *)&word.text, &cons_def->name);
OG_RETURN_IFERR(status);
status = lex_expected_fetch(lex, &word);
OG_RETURN_IFERR(status);
switch ((key_wid_t)word.id) {
case KEY_WORD_PRIMARY:
status = sql_parse_primary_unique_cons(stmt, lex, CONS_TYPE_PRIMARY, cons_def);
break;
case KEY_WORD_FOREIGN:
status = sql_parse_foreign_key(stmt, lex, cons_def);
break;
case KEY_WORD_CHECK:
status = sql_parse_check_outline_cons(stmt, lex, def, cons_def);
break;
case KEY_WORD_UNIQUE:
status = sql_parse_primary_unique_cons(stmt, lex, CONS_TYPE_UNIQUE, cons_def);
break;
default:
OG_SRC_THROW_ERROR_EX(word.text.loc, ERR_SQL_SYNTAX_ERROR, ", expected but %s found", W2S(&word));
status = OG_ERROR;
break;
}
return status;
}
status_t sql_parse_auto_primary_key_constr_name(sql_stmt_t *stmt, text_t *constr_name, text_t *sch_name,
text_t *tab_name)
{
text_t md5_text;
uint32 len;
uint32 max_len;
char name[DDL_MAX_CONSTR_NAME_LEN + 1] = { 0 };
char md5_name[OG_MD5_SIZE + 1] = { 0 };
uchar digest[OG_MD5_HASH_SIZE] = { 0 };
binary_t bin = {
.bytes = digest,
.size = OG_MD5_HASH_SIZE
};
len = 0;
max_len = DDL_MAX_CONSTR_NAME_LEN + 1;
MEMS_RETURN_IFERR(strncpy_s(name, max_len, sch_name->str, sch_name->len));
len += sch_name->len;
MEMS_RETURN_IFERR(strncpy_s(name + len, max_len - len, tab_name->str, tab_name->len));
len += tab_name->len;
cm_calc_md5((const uchar *)&name, len, (uchar *)digest, &bin.size);
md5_text.str = md5_name;
md5_text.len = (uint32)sizeof(md5_name);
if (cm_bin2text(&bin, OG_FALSE, &md5_text) != OG_SUCCESS) {
return OG_ERROR;
}
PRTS_RETURN_IFERR(snprintf_s(constr_name->str, OG_MAX_NAME_LEN, OG_MAX_NAME_LEN - 1, "_PK_SYS_"));
MEMS_RETURN_IFERR(
strncat_s(constr_name->str, OG_MAX_NAME_LEN - strlen(constr_name->str), md5_text.str, md5_text.len));
constr_name->len = (uint32)strlen(constr_name->str);
return OG_SUCCESS;
}
static status_t sql_try_parse_primary_unique_cons(sql_stmt_t *stmt, lex_t *lex, constraint_type_t type,
knl_table_def_t *def, bool32 *result)
{
status_t status;
word_t word;
knl_constraint_def_t *cons_def = NULL;
if (type == CONS_TYPE_PRIMARY) {
status = lex_try_fetch(lex, "KEY", result);
OG_RETURN_IFERR(status);
if (*result == OG_FALSE) {
return OG_SUCCESS;
}
status = sql_alloc_inline_cons(stmt, type, &def->constraints, &cons_def);
OG_RETURN_IFERR(status);
cons_def->index.primary = OG_TRUE;
cons_def->index.cr_mode = OG_INVALID_ID8;
cons_def->index.pctfree = OG_INVALID_ID32;
status = sql_parse_column_list(stmt, lex, &cons_def->columns, OG_FALSE, NULL);
OG_RETURN_IFERR(status);
} else {
LEX_SAVE(lex);
status = lex_try_fetch_bracket(lex, &word, result);
OG_RETURN_IFERR(status);
if (*result == OG_FALSE) {
return OG_SUCCESS;
}
LEX_RESTORE(lex);
status = sql_alloc_inline_cons(stmt, type, &def->constraints, &cons_def);
OG_RETURN_IFERR(status);
cons_def->index.unique = OG_TRUE;
cons_def->index.cr_mode = OG_INVALID_ID8;
cons_def->index.pctfree = OG_INVALID_ID32;
status = sql_parse_column_list(stmt, lex, &cons_def->columns, OG_FALSE, NULL);
OG_RETURN_IFERR(status);
}
OG_RETURN_IFERR(lex_fetch(lex, &word));
if (word.type == WORD_TYPE_EOF || IS_SPEC_CHAR(&word, ',')) {
lex_back(lex, &word);
return OG_SUCCESS;
}
switch (word.id) {
case KEY_WORD_USING:
status = lex_expected_fetch_word(lex, "index");
OG_RETURN_IFERR(status);
return sql_parse_index_attrs(stmt, lex, &cons_def->index);
case KEY_WORD_ENABLE:
case KEY_WORD_DISABLE:
case KEY_WORD_NOT:
OG_THROW_ERROR(ERR_CAPABILITY_NOT_SUPPORT, "key word \"not\" for constraints");
return OG_ERROR;
default:
OG_SRC_THROW_ERROR_EX(word.text.loc, ERR_SQL_SYNTAX_ERROR, "unexpected text %s", W2S(&word));
return OG_ERROR;
}
}
status_t sql_append_primary_key_cols(sql_stmt_t *stmt, text_t *ref_user, text_t *ref_table,
galist_t *ref_columns)
{
knl_dictionary_t dc;
uint32 i;
uint32 count;
status_t status = OG_SUCCESS;
knl_index_desc_t *index_desc = NULL;
knl_column_t *column = NULL;
knl_index_col_def_t *index_column = NULL;
if (ref_columns->count != 0) {
return OG_SUCCESS;
}
if (OG_SUCCESS != knl_open_dc(stmt->session, ref_user, ref_table, &dc)) {
return OG_ERROR;
}
count = knl_get_index_count(dc.handle);
for (i = 0; i < count; i++) {
index_desc = knl_get_index(dc.handle, i);
if (index_desc->primary) {
break;
}
}
if (i == count) {
knl_close_dc(&dc);
OG_THROW_ERROR(ERR_REFERENCED_NO_PRIMARY_KEY);
return OG_ERROR;
}
for (i = 0; i < index_desc->column_count; i++) {
column = knl_get_column(dc.handle, index_desc->columns[i]);
status = cm_galist_new(ref_columns, sizeof(knl_index_col_def_t), (void **)&index_column);
OG_BREAK_IF_ERROR(status);
index_column->mode = SORT_MODE_ASC;
status = sql_copy_str(stmt->context, column->name, &index_column->name);
OG_BREAK_IF_ERROR(status);
}
knl_close_dc(&dc);
return status;
}
status_t sql_parse_references_clause(sql_stmt_t *stmt, lex_t *lex, text_t *ref_user, text_t *ref_table,
knl_refactor_t *refactor, galist_t *ref_columns)
{
bool32 result = OG_FALSE;
word_t word;
status_t status;
lex->flags = LEX_WITH_OWNER;
status = lex_expected_fetch_variant(lex, &word);
OG_RETURN_IFERR(status);
status = sql_convert_object_name(stmt, &word, ref_user, NULL, ref_table);
OG_RETURN_IFERR(status);
cm_galist_init(ref_columns, stmt->context, sql_alloc_mem);
if (lex_try_fetch_bracket(lex, &word, &result) != OG_SUCCESS) {
return OG_ERROR;
}
if (result) {
lex_back(lex, &word);
status = sql_parse_column_list(stmt, lex, ref_columns, OG_FALSE, NULL);
OG_RETURN_IFERR(status);
}
*refactor = REF_DEL_NOT_ALLOWED;
if (lex_try_fetch(lex, "ON", &result) != OG_SUCCESS) {
return OG_ERROR;
}
if (result) {
if (OG_SUCCESS != lex_expected_fetch_word(lex, "DELETE")) {
return OG_ERROR;
}
status = lex_expected_fetch(lex, &word);
OG_RETURN_IFERR(status);
if (word.id == KEY_WORD_CASCADE) {
*refactor |= REF_DEL_CASCADE;
} else if (word.id == KEY_WORD_SET) {
status = lex_expected_fetch_word(lex, "NULL");
OG_RETURN_IFERR(status);
*refactor |= REF_DEL_SET_NULL;
} else {
OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "CASCADE/SET NULL expected but %s found.", W2S(&word));
return OG_ERROR;
}
}
return sql_append_primary_key_cols(stmt, ref_user, ref_table, ref_columns);
}
static status_t sql_try_parse_foreign_key(sql_stmt_t *stmt, lex_t *lex, knl_table_def_t *def, bool32 *result)
{
status_t status;
knl_reference_def_t *ref = NULL;
knl_constraint_def_t *cons_def = NULL;
if (lex_try_fetch(lex, "KEY", result) != OG_SUCCESS) {
return OG_ERROR;
}
if (*result == OG_FALSE) {
return OG_SUCCESS;
}
status = sql_alloc_inline_cons(stmt, CONS_TYPE_REFERENCE, &def->constraints, &cons_def);
OG_RETURN_IFERR(status);
ref = &cons_def->ref;
if (sql_parse_column_list(stmt, lex, &cons_def->columns, OG_FALSE, NULL) != OG_SUCCESS) {
return OG_ERROR;
}
if (lex_expected_fetch_word(lex, "REFERENCES") != OG_SUCCESS) {
return OG_ERROR;
}
if (sql_parse_references_clause(stmt, lex, &ref->ref_user, &ref->ref_table, &ref->refactor, &ref->ref_columns) !=
OG_SUCCESS) {
return OG_ERROR;
}
if (cons_def->columns.count != ref->ref_columns.count) {
OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "number of referencing columns must match referenced columns.");
return OG_ERROR;
}
return OG_SUCCESS;
}
static status_t sql_parse_column_ref(sql_stmt_t *stmt, lex_t *lex, knl_column_def_t *column, word_t *word,
uint32 *ex_flags)
{
if (*ex_flags & COLUMN_EX_REF) {
OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR,
"duplicate or conflicting references specifications");
return OG_ERROR;
}
*ex_flags |= COLUMN_EX_REF;
column->is_ref = OG_TRUE;
if (OG_SUCCESS != sql_parse_references_clause(stmt, lex, &column->ref_user, &column->ref_table, &column->refactor,
&column->ref_columns)) {
return OG_ERROR;
}
if (column->ref_columns.count != 1) {
OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "number of referencing columns must match referenced columns.");
return OG_ERROR;
}
return OG_SUCCESS;
}
static status_t sql_parse_column_check(sql_stmt_t *stmt, lex_t *lex, knl_column_def_t *column, word_t *word,
uint32 *ex_flags)
{
status_t status;
uint32 save_flags;
cond_tree_t *cond = NULL;
if (*ex_flags & COLUMN_EX_CHECK) {
OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, "duplicate or conflicting check specifications");
return OG_ERROR;
}
status = lex_expected_fetch_bracket(lex, word);
OG_RETURN_IFERR(status);
OG_RETURN_IFERR(lex_push(lex, &word->text));
column->check_text = word->text.value;
cm_trim_text(&column->check_text);
save_flags = lex->flags;
lex->flags = LEX_WITH_ARG | LEX_WITH_OWNER;
stmt->is_check = OG_TRUE;
status = sql_create_cond_until(stmt, &cond, word);
stmt->is_check = OG_FALSE;
lex_pop(lex);
OG_RETURN_IFERR(status);
if (word->type != WORD_TYPE_EOF) {
OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, "EOF expected but %s found",
T2S(&word->text.value));
return OG_ERROR;
}
column->is_check = OG_TRUE;
*ex_flags |= COLUMN_EX_CHECK;
lex->flags = save_flags;
column->check_cond = cond;
return OG_SUCCESS;
}
static status_t sql_parse_column_not_null(sql_stmt_t *stmt, lex_t *lex, knl_column_def_t *column, word_t *word,
uint32 *ex_flags)
{
if (*ex_flags & COLUMN_EX_NULLABLE) {
OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR,
"duplicate or conflicting not null/null specifications");
return OG_ERROR;
}
if (lex_expected_fetch_word(lex, "NULL") != OG_SUCCESS) {
return OG_ERROR;
}
*ex_flags |= COLUMN_EX_NULLABLE;
column->nullable = OG_FALSE;
column->has_null = OG_TRUE;
return OG_SUCCESS;
}
static status_t sql_parse_column_primary(sql_stmt_t *stmt, lex_t *lex, knl_column_def_t *column, word_t *word,
uint32 *ex_flags)
{
if (*ex_flags & COLUMN_EX_KEY) {
OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "duplicate or conflicting primary key/unique specifications");
return OG_ERROR;
}
CHECK_CONS_TZ_TYPE_RETURN(column->datatype);
if (lex_expected_fetch_word(lex, "KEY") != OG_SUCCESS) {
return OG_ERROR;
}
*ex_flags |= COLUMN_EX_KEY;
column->primary = OG_TRUE;
column->nullable = OG_FALSE;
column->has_null = OG_TRUE;
return OG_SUCCESS;
}
status_t sql_parse_inline_constraint_elemt(sql_stmt_t *stmt, lex_t *lex, knl_column_def_t *column, word_t *word,
uint32 *ex_flags, text_t *cons_name)
{
status_t status;
switch (word->id) {
case KEY_WORD_NOT:
status = sql_parse_column_not_null(stmt, lex, column, word, ex_flags);
OG_RETURN_IFERR(status);
break;
case KEY_WORD_PRIMARY:
status = sql_parse_column_primary(stmt, lex, column, word, ex_flags);
OG_RETURN_IFERR(status);
if (cons_name != NULL) {
column->inl_pri_cons_name = *cons_name;
}
break;
case RES_WORD_NULL:
if (*ex_flags & COLUMN_EX_NULLABLE) {
OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR,
"duplicate or conflicting not null/null specifications");
return OG_ERROR;
}
column->has_null = OG_TRUE;
*ex_flags |= COLUMN_EX_NULLABLE;
break;
case KEY_WORD_REFERENCES:
status = sql_parse_column_ref(stmt, lex, column, word, ex_flags);
OG_RETURN_IFERR(status);
if (cons_name != NULL) {
column->inl_ref_cons_name = *cons_name;
}
break;
case KEY_WORD_UNIQUE:
if (*ex_flags & COLUMN_EX_KEY) {
OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR,
"duplicate or conflicting primary key/unique specifications");
return OG_ERROR;
}
CHECK_CONS_TZ_TYPE_RETURN(column->datatype);
*ex_flags |= COLUMN_EX_KEY;
column->unique = OG_TRUE;
if (cons_name != NULL) {
column->inl_uq_cons_name = *cons_name;
}
break;
case KEY_WORD_CHECK:
status = sql_parse_column_check(stmt, lex, column, word, ex_flags);
OG_RETURN_IFERR(status);
if (cons_name != NULL) {
column->inl_chk_cons_name = *cons_name;
}
break;
default:
OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, "constraint expected but %s found", W2S(word));
return OG_ERROR;
}
return OG_SUCCESS;
}
status_t sql_parse_inline_constraint(sql_stmt_t *stmt, lex_t *lex, knl_column_def_t *column, word_t *word,
uint32 *ex_flags)
{
text_t inl_constr = {
.str = NULL,
.len = 0
};
if (lex_expected_fetch_variant(lex, word) != OG_SUCCESS) {
return OG_ERROR;
}
if (sql_copy_object_name(stmt->context, word->type, (text_t *)&word->text, &inl_constr) != OG_SUCCESS) {
return OG_ERROR;
}
if (lex_fetch(lex, word) != OG_SUCCESS) {
return OG_ERROR;
}
return sql_parse_inline_constraint_elemt(stmt, lex, column, word, ex_flags, &inl_constr);
}
static status_t sql_parse_out_line_constraint(sql_stmt_t *stmt, lex_t *lex, knl_table_def_t *def, word_t *word,
bool32 *res)
{
status_t status;
switch (word->id) {
case KEY_WORD_CONSTRAINT:
status = sql_try_parse_constraint(stmt, lex, def, res);
break;
case KEY_WORD_PRIMARY:
status = sql_try_parse_primary_unique_cons(stmt, lex, CONS_TYPE_PRIMARY, def, res);
break;
case KEY_WORD_UNIQUE:
status = sql_try_parse_primary_unique_cons(stmt, lex, CONS_TYPE_UNIQUE, def, res);
break;
case KEY_WORD_CHECK:
*res = OG_TRUE;
status = sql_parse_check_outline_cons(stmt, lex, def, NULL);
break;
case KEY_WORD_FOREIGN:
default:
status = sql_try_parse_foreign_key(stmt, lex, def, res);
break;
}
return status;
}
status_t sql_try_parse_cons(sql_stmt_t *stmt, lex_t *lex, knl_table_def_t *def, word_t *word, bool32 *result)
{
status_t status;
*result = OG_FALSE;
if (!IS_CONSTRAINT_KEYWORD(word->id)) {
return OG_SUCCESS;
}
status = sql_parse_out_line_constraint(stmt, lex, def, word, result);
OG_RETURN_IFERR(status);
if (*result == OG_FALSE) {
return OG_SUCCESS;
}
status = lex_fetch(lex, word);
OG_RETURN_IFERR(status);
if (word->type == WORD_TYPE_EOF || IS_SPEC_CHAR(word, ',')) {
return OG_SUCCESS;
}
OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, ", expected but %s found", W2S(word));
return OG_ERROR;
}
status_t sql_verify_check_constraint(sql_stmt_t *stmt, knl_table_def_t *def)
{
uint32 loop;
knl_constraint_def_t *cons_def = NULL;
knl_column_def_t *col = NULL;
for (loop = 0; loop < def->constraints.count; loop++) {
cons_def = (knl_constraint_def_t *)cm_galist_get(&def->constraints, loop);
if (cons_def->type != CONS_TYPE_CHECK) {
continue;
}
OG_RETURN_IFERR(sql_verify_outline_check(stmt, def, cons_def, (cond_tree_t *)cons_def->check.cond));
}
for (loop = 0; loop < def->columns.count; loop++) {
col = (knl_column_def_t *)cm_galist_get(&def->columns, loop);
if (!col->is_check) {
continue;
}
OG_RETURN_IFERR(sql_verify_inline_check(stmt, col, (cond_tree_t *)col->check_cond));
}
return OG_SUCCESS;
}
static status_t sql_altable_inline_check_cons(sql_stmt_t *stmt, knl_column_def_t *column,
knl_alt_column_prop_t *column_def)
{
knl_constraint_def_t *cons_def = NULL;
if (sql_verify_inline_check(stmt, column, column->check_cond) != OG_SUCCESS) {
return OG_ERROR;
}
if (sql_alloc_inline_cons(stmt, CONS_TYPE_CHECK, &column_def->constraints, &cons_def) != OG_SUCCESS) {
return OG_ERROR;
}
if (sql_append_inline_checks_column(stmt, cons_def, column) != OG_SUCCESS) {
return OG_ERROR;
}
return OG_SUCCESS;
}
static status_t sql_altable_inline_cons_index(sql_stmt_t *stmt, knl_column_def_t *column,
knl_alt_column_prop_t *column_def)
{
knl_constraint_def_t *cons_def = NULL;
knl_index_col_def_t *index_col = NULL;
constraint_type_t type;
type = column->primary ? CONS_TYPE_PRIMARY : CONS_TYPE_UNIQUE;
if (sql_alloc_inline_cons(stmt, type, &column_def->constraints, &cons_def) != OG_SUCCESS) {
return OG_ERROR;
}
if (column->unique) {
if (column->inl_uq_cons_name.len != 0) {
cons_def->name = column->inl_uq_cons_name;
cons_def->cons_state.is_anonymous = OG_FALSE;
}
} else {
if (column->inl_pri_cons_name.len != 0) {
cons_def->name = column->inl_pri_cons_name;
cons_def->cons_state.is_anonymous = OG_FALSE;
}
}
cm_galist_init(&cons_def->columns, stmt->context, sql_alloc_mem);
OG_RETURN_IFERR(cm_galist_new(&cons_def->columns, sizeof(knl_index_col_def_t), (pointer_t *)&index_col));
index_col->mode = SORT_MODE_ASC;
index_col->name = column->name;
cons_def->index.primary = column->primary;
cons_def->index.unique = column->unique;
cons_def->index.cr_mode = OG_INVALID_ID8;
cons_def->index.pctfree = OG_INVALID_ID32;
return OG_SUCCESS;
}
status_t sql_create_altable_inline_cons(sql_stmt_t *stmt, knl_column_def_t *column, knl_alt_column_prop_t *column_def)
{
cm_galist_init(&column_def->constraints, stmt->context, sql_alloc_mem);
if (column->is_check) {
if (sql_altable_inline_check_cons(stmt, column, column_def) != OG_SUCCESS) {
return OG_ERROR;
}
}
if (column->unique || column->primary) {
if (sql_altable_inline_cons_index(stmt, column, column_def) != OG_SUCCESS) {
return OG_ERROR;
}
}
return OG_SUCCESS;
}
status_t sql_parse_altable_constraint_rename(sql_stmt_t *stmt, lex_t *lex, knl_altable_def_t *def)
{
key_wid_t wid;
word_t word;
uint32 pre_flags = lex->flags;
lex->flags = LEX_SINGLE_WORD;
def->action = ALTABLE_RENAME_CONSTRAINT;
if (lex_expected_fetch_variant(lex, &word) != OG_SUCCESS) {
return OG_ERROR;
}
if (sql_copy_object_name(stmt->context, word.type, (text_t *)&word.text, &def->cons_def.name) != OG_SUCCESS) {
return OG_ERROR;
}
if (lex_expected_fetch(lex, &word) != OG_SUCCESS) {
return OG_ERROR;
}
wid = (key_wid_t)word.id;
if (word.type != WORD_TYPE_KEYWORD || wid != KEY_WORD_TO) {
OG_SRC_THROW_ERROR_EX(word.text.loc, ERR_SQL_SYNTAX_ERROR, "TO expected but %s found", W2S(&word));
return OG_ERROR;
}
if (lex_expected_fetch_variant(lex, &word) != OG_SUCCESS) {
return OG_ERROR;
}
if (sql_copy_object_name(stmt->context, word.type, (text_t *)&word.text, &def->cons_def.new_cons.name) !=
OG_SUCCESS) {
return OG_ERROR;
}
lex->flags = pre_flags;
return lex_expected_end(lex);
}
status_t og_parse_constraint_state(sql_stmt_t *stmt, knl_constraint_def_t *cons_def, galist_t *opts)
{
knl_constraint_state_t *cons_state = NULL;
constraint_state *state = NULL;
uint32 i;
if (opts == NULL) {
return OG_SUCCESS;
}
CM_POINTER2(stmt, cons_def);
cons_state = &cons_def->cons_state;
for (i = 0; i < opts->count; i++) {
state = (constraint_state *)cm_galist_get(opts, i);
switch (state->type) {
case CONS_STATE_ENABLE:
cons_state->is_enable = OG_TRUE;
break;
case CONS_STATE_DISABLE:
cons_state->is_enable = OG_FALSE;
break;
case CONS_STATE_DEFEREABLE:
cons_state->deferrable_ops = STATE_DEFERRABLE;
break;
case CONS_STATE_NOT_DEFEREABLE:
cons_state->deferrable_ops = STATE_NOT_DEFERRABLE;
break;
case CONS_STATE_INITIALLY_IMMEDIATE:
cons_state->initially_ops = STATE_INITIALLY_IMMEDIATE;
break;
case CONS_STATE_INITIALLY_DEFERRED:
cons_state->initially_ops = STATE_INITIALLY_DEFERRED;
break;
case CONS_STATE_RELY:
cons_state->rely_ops = STATE_RELY;
break;
case CONS_STATE_NO_RELY:
cons_state->rely_ops = STATE_NO_RELY;
break;
case CONS_STATE_VALIDATE:
cons_state->is_validate = OG_TRUE;
break;
case CONS_STATE_NO_VALIDATE:
cons_state->is_validate = OG_FALSE;
break;
case CONS_STATE_USING_INDEX:
if ((cons_def->type != CONS_TYPE_PRIMARY) && (cons_def->type != CONS_TYPE_UNIQUE)) {
OG_THROW_ERROR(ERR_SQL_SYNTAX_ERROR,
"\"USING INDEX\" cannot be specified in a constraint clause other than primary "
"key constraint and unique constraint.");
return OG_ERROR;
}
if (cons_state->is_use_index) {
OG_THROW_ERROR(ERR_SQL_SYNTAX_ERROR, "duplicate using index specification");
return OG_ERROR;
}
OG_RETURN_IFERR(og_parse_index_attrs(stmt, &cons_def->index, state->index_opts));
cons_state->is_use_index = OG_TRUE;
break;
case CONS_STATE_PARALLEL:
if ((cons_def->type != CONS_TYPE_PRIMARY) && (cons_def->type != CONS_TYPE_UNIQUE)) {
OG_THROW_ERROR(ERR_SQL_SYNTAX_ERROR,
"\"PARALLEL\" cannot be specified in a constraint clause other than primary "
"key constraint and unique constraint.");
return OG_ERROR;
}
if (cons_def->index.parallelism != 0) {
OG_THROW_ERROR(ERR_SQL_SYNTAX_ERROR, "duplicate parallelism specification");
return OG_ERROR;
}
cons_def->index.parallelism = state->parallelism;
break;
case CONS_STATE_REVERSE:
if ((cons_def->type != CONS_TYPE_PRIMARY) && (cons_def->type != CONS_TYPE_UNIQUE)) {
OG_THROW_ERROR(ERR_SQL_SYNTAX_ERROR,
"\"REVERSE\" cannot be specified in a constraint clause other than primary "
"key constraint and unique constraint.");
return OG_ERROR;
}
if (cons_def->index.is_reverse) {
OG_THROW_ERROR(ERR_SQL_SYNTAX_ERROR, "duplicate reverse specification");
return OG_ERROR;
}
cons_def->index.is_reverse = OG_TRUE;
break;
default:
break;
}
}
return OG_SUCCESS;
}
static status_t og_parse_primary_unique_cons(sql_stmt_t *stmt, knl_table_def_t *def, parse_constraint_t *cons)
{
status_t status;
knl_constraint_def_t *cons_def = NULL;
if (cons->type == CONS_TYPE_PRIMARY) {
status = sql_alloc_inline_cons(stmt, CONS_TYPE_PRIMARY, &def->constraints, &cons_def);
OG_RETURN_IFERR(status);
cons_def->index.primary = OG_TRUE;
cons_def->index.cr_mode = OG_INVALID_ID8;
cons_def->index.pctfree = OG_INVALID_ID32;
status = og_parse_column_list(stmt, &cons_def->columns, NULL, cons->column_list);
OG_RETURN_IFERR(status);
} else {
status = sql_alloc_inline_cons(stmt, CONS_TYPE_UNIQUE, &def->constraints, &cons_def);
OG_RETURN_IFERR(status);
cons_def->index.unique = OG_TRUE;
cons_def->index.cr_mode = OG_INVALID_ID8;
cons_def->index.pctfree = OG_INVALID_ID32;
status = og_parse_column_list(stmt, &cons_def->columns, NULL, cons->column_list);
OG_RETURN_IFERR(status);
}
if (cons->name != NULL) {
cons_def->cons_state.is_anonymous = OG_FALSE;
cons_def->name.str = cons->name;
cons_def->name.len = strlen(cons->name);
}
status = og_parse_constraint_state(stmt, cons_def, cons->state_opts);
OG_RETURN_IFERR(status);
return OG_SUCCESS;
}
static status_t og_parse_check_cons(sql_stmt_t *stmt, knl_table_def_t *def, parse_constraint_t *cons)
{
status_t status;
knl_constraint_def_t *cons_def = NULL;
status = sql_alloc_inline_cons(stmt, CONS_TYPE_CHECK, &def->constraints, &cons_def);
OG_RETURN_IFERR(status);
if (cons->name != NULL) {
cons_def->cons_state.is_anonymous = OG_FALSE;
cons_def->name.str = cons->name;
cons_def->name.len = strlen(cons->name);
}
cons_def->check.text = cons->check_text;
cons_def->check.cond = cons->cond;
OG_RETURN_IFERR(status);
return OG_SUCCESS;
}
static status_t og_parse_foreign_key(sql_stmt_t *stmt, knl_table_def_t *def, parse_constraint_t *cons)
{
status_t status;
knl_reference_def_t *ref = NULL;
knl_constraint_def_t *cons_def = NULL;
status = sql_alloc_inline_cons(stmt, CONS_TYPE_REFERENCE, &def->constraints, &cons_def);
OG_RETURN_IFERR(status);
ref = &cons_def->ref;
status = og_parse_column_list(stmt, &cons_def->columns, NULL, cons->cols);
OG_RETURN_IFERR(status);
if (cons->ref->owner.len != 0) {
ref->ref_user = cons->ref->owner;
} else {
cm_str2text(stmt->session->curr_schema, &ref->ref_user);
}
ref->ref_table = cons->ref->name;
ref->refactor = cons->refactor;
status = og_parse_column_list(stmt, &ref->ref_columns, NULL, cons->ref_cols);
OG_RETURN_IFERR(status);
status = sql_append_primary_key_cols(stmt, &ref->ref_user, &ref->ref_table, &ref->ref_columns);
OG_RETURN_IFERR(status);
if (cons_def->columns.count != ref->ref_columns.count) {
OG_THROW_ERROR(ERR_SQL_SYNTAX_ERROR,
"foreign key columns count must be equal to primary key columns count");
return OG_ERROR;
}
if (cons->name != NULL) {
cons_def->cons_state.is_anonymous = OG_FALSE;
cons_def->name.str = cons->name;
cons_def->name.len = strlen(cons->name);
}
return OG_SUCCESS;
}
status_t og_try_parse_cons(sql_stmt_t *stmt, knl_table_def_t *def, parse_constraint_t *cons)
{
switch (cons->type) {
case CONS_TYPE_PRIMARY:
return og_parse_primary_unique_cons(stmt, def, cons);
case CONS_TYPE_UNIQUE:
return og_parse_primary_unique_cons(stmt, def, cons);
case CONS_TYPE_REFERENCE:
return og_parse_foreign_key(stmt, def, cons);
case CONS_TYPE_CHECK:
return og_parse_check_cons(stmt, def, cons);
default:
return OG_ERROR;
}
}