/* * PROGRAM: JRD Command Oriented Query Language * MODULE: parse.c * DESCRIPTION: Statement parser * * The contents of this file are subject to the Interbase Public * License Version 1.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.Inprise.com/IPL.html * * Software distributed under the License is distributed on an * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express * or implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code was created by Inprise Corporation * and its predecessors. Portions created by Inprise Corporation are * Copyright (C) Inprise Corporation. * * All Rights Reserved. * Contributor(s): ______________________________________. */ #include "firebird.h" #include "../jrd/ib_stdio.h" #include #include #define PARSER_MAIN #include "../qli/dtr.h" #include "../qli/exe.h" // This is only included to suppress a compiler warning #include "../qli/parse.h" #include "../qli/compile.h" #include "../qli/report.h" #include "../qli/all_proto.h" #include "../qli/err_proto.h" #include "../qli/hsh_proto.h" #include "../qli/lex_proto.h" #include "../qli/mov_proto.h" #include "../qli/parse_proto.h" #include "../qli/proc_proto.h" #define SYNTAX_NODE(type,count) syntax_node (type, count) #define KEYWORD(kw) (QLI_token->tok_keyword == kw) #define INT_CAST (SYN) (IPTR) static void check_end(void); static void command_end(void); static DBB get_dbb(SYM); static SYN make_list(LLS); static NAM make_name(void); static CON make_numeric_constant(TEXT *, USHORT); static TEXT *make_string(TEXT *, USHORT); static SYN negate(SYN); static KWWORDS next_keyword(void); static SYN parse_abort(void); static SYN parse_accept(void); static SYN parse_add(USHORT *, USHORT *); static SYN parse_and(USHORT *); static SYN parse_assignment(void); static SYN parse_boolean(USHORT *); static SYN parse_copy(void); static DBB parse_database(void); static SYN parse_declare(void); static SYN parse_define(void); static SYN parse_def_index(void); static SYN parse_def_relation(void); static SYN parse_delete(void); static SYN parse_drop(void); static int parse_dtype(USHORT *, USHORT *); static int parse_dtype_subtype(void); static SYN parse_edit(void); static TEXT *parse_edit_string(void); static SYN parse_erase(void); static SYN parse_extract(void); static QLI_FLD parse_field(int); static SYN parse_field_name(SYN *); static SYN parse_for(void); #ifdef PYXIS static SYN parse_form(void); #endif static SYN parse_from(USHORT *, USHORT *); static SYN parse_function(void); static TEXT *parse_header(void); static SYN parse_help(void); static SYN parse_if(void); static SYN parse_in(SYN, NOD_T, USHORT); static SYN parse_insert(void); static NOD_T parse_join_type(void); static SYN parse_list_fields(void); static CON parse_literal(void); static SYN parse_matches(void); static void parse_matching_paren(void); static SYN parse_menu(void); static SYN parse_modify(void); static SYN parse_modify_index(void); static SYN parse_modify_relation(void); static SYN parse_multiply(USHORT *, USHORT *); static NAM parse_name(void); static SYN parse_not(USHORT *); static int parse_ordinal(void); static SYN parse_output(void); static SYN parse_primitive_value(USHORT *, USHORT *); static SYN parse_print_list(void); static SYN parse_print(void); static SYN parse_prompt(void); static QFL parse_qualified_filter(void); static QFN parse_qualified_function(void); static QPR parse_qualified_procedure(void); static QLI_REL parse_qualified_relation(void); static SYN parse_ready(NOD_T); static SYN parse_relational(USHORT *); static SYN parse_relation(void); static SYN parse_rename(void); static SYN parse_repeat(void); static SYN parse_report(void); static SYN parse_rse(void); static SYN parse_select(void); static SYN parse_set(void); static SYN parse_shell(void); static SYN parse_show(void); static SYN parse_sort(void); static SYN parse_sql_alter(void); static SYN parse_sql_create(void); static int parse_sql_dtype(USHORT *, USHORT *); static QLI_FLD parse_sql_field(void); static SYN parse_sql_grant_revoke(USHORT); static SYN parse_sql_index_create(USHORT, USHORT); static SYN parse_sql_joined_relation(SYN); static SYN parse_sql_join_clause(SYN); static SYN parse_sql_table_create(void); #ifdef NOT_USED_OR_REPLACED static SYN parse_sql_view_create(void); #endif static SYN parse_sql_relation(void); static SYN parse_sql_rse(void); static SYN parse_sql_singleton_select(void); static SYN parse_sql_subquery(void); static SYN parse_statement(void); static SYN parse_statistical(void); static SYN parse_store(void); static TEXT *parse_string(void); static SYM parse_symbol(void); static void parse_terminating_parens(USHORT *, USHORT *); static SYN parse_transaction(NOD_T); static SYN parse_udf_or_field(void); static SYN parse_update(void); static SYN parse_value(USHORT *, USHORT *); static int potential_rse(void); static QLI_REL resolve_relation(SYM, SYM); static SYN syntax_node(NOD_T, USHORT); static int test_end(void); typedef struct { SLONG gds_quad_high; ULONG gds_quad_low; } gds_quad; /* The following flags are: sql_flag indicates whether we are parsing in a SQL environment. The flag is used to turn off automatic end-of-command recognition. else_count indicates the depth of if/then/else's sw_report indicates whether we're in a report statement sw_statement indicates that we're actively parsing a statement/command sw_sql_view indicates parsing a SQL VIEW, so restrict the select. */ static USHORT sql_flag, else_count, sw_report, sw_statement, sw_sql_view; static SSHORT function_count; // indicates the depth of UDF calls static struct nod_types { KWWORDS nod_t_keyword; NOD_T nod_t_node; NOD_T nod_t_rpt_node; NOD_T nod_t_sql_node; } statisticals[] = { { KW_MAX, nod_max, nod_rpt_max, nod_agg_max }, { KW_MIN, nod_min, nod_rpt_min, nod_agg_min }, { KW_COUNT, nod_count, nod_rpt_count, nod_agg_count }, { KW_AVERAGE, nod_average, nod_rpt_average, nod_agg_average }, { KW_TOTAL, nod_total, nod_rpt_total, nod_agg_total }, { KW_none, nod_any, nod_any, nod_any } }; static NOD_T relationals[] = { nod_eql, nod_neq, nod_leq, nod_lss, nod_gtr, nod_geq, nod_containing, nod_like, nod_starts, nod_missing, nod_between, nod_sleuth, nod_matches, nod_and, nod_or, nod_not, nod_any, nod_unique, (NOD_T) 0 }; SYN PARQ_parse(void) { /************************************** * * P A R Q _ p a r s e * ************************************** * * Functional description * Parse a single statement or command. * **************************************/ LLS stack; SYN node; sql_flag = else_count = sw_report = 0; sw_statement = TRUE; switch (next_keyword()) { case KW_COMMIT: return parse_transaction(nod_commit); case KW_COPY: return parse_copy(); case KW_EXIT: return SYNTAX_NODE(nod_exit, 0); case KW_EXTRACT: return parse_extract(); case KW_QUIT: return SYNTAX_NODE(nod_quit, 0); case KW_DELETE: case KW_DROP: if (node = parse_drop()) return node; node = parse_delete(); check_end(); if (!MATCH(KW_THEN)) return node; stack = NULL; LLS_PUSH(node, &stack); LLS_PUSH(parse_statement(), &stack); return make_list(stack); case KW_DEFINE: return parse_define(); case KW_CREATE: return parse_sql_create(); case KW_ALTER: return parse_sql_alter(); case KW_EDIT: return parse_edit(); case KW_FINISH: return parse_transaction(nod_finish); case KW_GRANT: return parse_sql_grant_revoke(nod_sql_grant); case KW_HELP: return parse_help(); case KW_PREPARE: return parse_transaction(nod_prepare); case KW_READY: return parse_ready(nod_ready); case KW_RENAME: return parse_rename(); case KW_REVOKE: return parse_sql_grant_revoke(nod_sql_revoke); case KW_ROLLBACK: return parse_transaction(nod_rollback); case KW_SET: return parse_set(); case KW_SHELL: return parse_shell(); case KW_SHOW: return parse_show(); } return parse_statement(); } int PAR_match( KWWORDS keyword) { /************************************** * * P A R _ m a t c h * ************************************** * * Functional description * Test the current token for a particular keyword. * If so, advance the token stream. * **************************************/ SYM symbol; if (KEYWORD(keyword)) { PAR_token(); return TRUE; } for (symbol = QLI_token->tok_symbol; symbol; symbol = symbol->sym_homonym) if (symbol->sym_type == SYM_keyword && symbol->sym_keyword == (int) keyword) { PAR_token(); return TRUE; } return FALSE; } void PAR_real(void) { /************************************** * * P A R _ r e a l * ************************************** * * Functional description * Get a real (not EOL) token. * * If the token is an end of line, get the next token. * If the next token is a colon, start reading the * procedure. * **************************************/ DBB database; while ((QLI_token->tok_type == tok_eol) || KEYWORD(KW_continuation)) LEX_token(); if (MATCH(KW_COLON)) { database = parse_database(); PRO_invoke(database, QLI_token->tok_string); } } void PAR_real_token(void) { /************************************** * * P A R _ r e a l _ t o k e n * ************************************** * * Functional description * Composition of PAR_token followed by PAR_real. * **************************************/ PAR_token(); PAR_real(); } void PAR_token(void) { /************************************** * * P A R _ t o k e n * ************************************** * * Functional description * Get the next token. * If it's a procedure invocation, handle it * or complain and get rid of the evidence. * **************************************/ DBB database; for (;;) { LEX_token(); if (!(KEYWORD(KW_continuation)) && !(sw_statement && QLI_semi && QLI_token->tok_type == tok_eol)) break; } if (MATCH(KW_COLON)) { if (!QLI_databases) { ERRQ_error_format(159, NULL, NULL, NULL, NULL, NULL); // Msg159 no databases are ready ERRQ_pending(); LEX_token(); } else { database = parse_database(); PRO_invoke(database, QLI_token->tok_string); } } } static void check_end(void) { /************************************** * * c h e c k _ e n d * ************************************** * * Functional description * Check for end of statement. If it isn't complain bitterly. * **************************************/ if (QLI_token->tok_type == tok_eol || KEYWORD(KW_SEMI) || KEYWORD(KW_THEN) || (else_count && KEYWORD(KW_ELSE))) { sw_statement = FALSE; return; } SYNTAX_ERROR(161); // Msg161 end of statement } static void command_end(void) { /************************************** * * c o m m a n d _ e n d * ************************************** * * Functional description * Check for end of command. If it isn't complain bitterly. * **************************************/ if (QLI_token->tok_type == tok_eol || KEYWORD(KW_SEMI)) { sw_statement = FALSE; return; } SYNTAX_ERROR(162); // Msg162 end of command } static DBB get_dbb( SYM symbol) { /************************************** * * g e t _ d b b * ************************************** * * Functional description * Find a database block from a symbol * or its homonyms. * **************************************/ for (; symbol; symbol = symbol->sym_homonym) if (symbol->sym_type == SYM_database) return (DBB) symbol->sym_object; return NULL; } static SYN make_list( LLS stack) { /************************************** * * m a k e _ l i s t * ************************************** * * Functional description * Dump a stack of junk into a list node. Best count * them first. * **************************************/ SYN node, *ptr; LLS temp; USHORT count; temp = stack; count = 0; while (temp) { count++; temp = temp->lls_next; } node = SYNTAX_NODE(nod_list, count); ptr = &node->syn_arg[count]; while (stack) *--ptr = (SYN) LLS_POP(&stack); return node; } static NAM make_name(void) { /************************************** * * m a k e _ n a m e * ************************************** * * Functional description * Generate a unique name for something * (like a database) that needs one. * **************************************/ NAM name; SSHORT l, i; TEXT c, *p, *q, string[32]; for (i = 0; i < 1000; i++) { sprintf(string, "QLI_%d", i); if (i < 10) l = 5; else l = (i < 100) ? 6 : 7; if (!(HSH_lookup(string, l))) break; } name = (NAM) ALLOCDV(type_nam, l); name->nam_length = l; p = name->nam_string; q = string; if (l) do { c = *q++; *p++ = UPPER(c); } while (--l); return name; } static CON make_numeric_constant( TEXT * string, USHORT length) { /************************************** * * m a k e _ n u m e r i c _ c o n s t a n t * ************************************** * * Functional description * Build a constant block for a numeric * constant. Numeric constants are normally * stored as long words, but especially large * ones become text. They ought to become * double precision, one would think, but they'd * have to be VAX style double precision which is * more pain than gain. * **************************************/ CON constant; TEXT *p; USHORT l; p = string; l = length; /* If there are a reasonable number of digits, convert to binary */ if (length < 9) { constant = (CON) ALLOCDV(type_con, sizeof(SLONG)); constant->con_desc.dsc_dtype = dtype_long; constant->con_desc.dsc_length = sizeof(SLONG); constant->con_desc.dsc_address = constant->con_data; constant->con_desc.dsc_scale = MOVQ_decompose(string, length, (SLONG*) constant->con_data); } else { ++length; constant = (CON) ALLOCDV(type_con, length); constant->con_desc.dsc_dtype = dtype_text; constant->con_desc.dsc_length = length; constant->con_desc.dsc_address = constant->con_data; p = (TEXT *) constant->con_desc.dsc_address; *p++ = '0'; while (--length) *p++ = *string++; } return constant; } static TEXT *make_string( TEXT * address, USHORT length) { /************************************** * * m a k e _ s t r i n g * ************************************** * * Functional description * Copy a string into a temporary string block. * **************************************/ STR string; TEXT *p; string = (STR) ALLOCDV(type_str, length); p = string->str_data; if (length) do *p++ = *address++; while (--length); return string->str_data; } static SYN negate( SYN expr) { /************************************** * * n e g a t e * ************************************** * * Functional description * Build negation of expression. * **************************************/ SYN node; node = SYNTAX_NODE(nod_not, 1); node->syn_arg[0] = expr; return node; } static KWWORDS next_keyword(void) { /************************************** * * n e x t _ k e y w o r d * ************************************** * * Functional description * Get a real token and return the keyword number. * **************************************/ SYM symbol; PAR_real(); for (symbol = QLI_token->tok_symbol; symbol; symbol = symbol->sym_homonym) if (symbol->sym_type == SYM_keyword) return (KWWORDS) symbol->sym_keyword; return KW_none; } static SYN parse_abort(void) { /************************************** * * p a r s e _ a b o r t * ************************************** * * Functional description * Parse an ABORT statement. * **************************************/ SYN node; PAR_token(); node = SYNTAX_NODE(nod_abort, 1); if (KEYWORD(KW_SEMI)) node->syn_count = 0; else node->syn_arg[0] = parse_value(0, 0); return node; } static SYN parse_accept(void) { /************************************** * * p a r s e _ a c c e p t * ************************************** * * Functional description * Parse form update statement. * **************************************/ IBERROR(484); // FORMs not supported return 0; } static SYN parse_add( USHORT * paren_count, USHORT * bool_flag) { /************************************** * * p a r s e _ a d d * ************************************** * * Functional description * Parse the lowest precedence operatrs, ADD and SUBTRACT. * **************************************/ SYN node, arg; NOD_T operatr; node = parse_multiply(paren_count, bool_flag); while (true) { if (MATCH(KW_PLUS)) operatr = nod_add; else if (MATCH(KW_MINUS)) operatr = nod_subtract; else return node; arg = node; node = SYNTAX_NODE(operatr, 2); node->syn_arg[0] = arg; node->syn_arg[1] = parse_multiply(paren_count, bool_flag); } } static SYN parse_and( USHORT * paren_count) { /************************************** * * p a r s e _ a n d * ************************************** * * Functional description * Parse an AND expression. * **************************************/ SYN expr, node; expr = parse_not(paren_count); /* while (*paren_count && KEYWORD (KW_RIGHT_PAREN)) { parse_matching_paren(); (*paren_count)--; } */ if (!MATCH(KW_AND)) return expr; node = SYNTAX_NODE(nod_and, 2); node->syn_arg[0] = expr; node->syn_arg[1] = parse_and(paren_count); return node; } static SYN parse_assignment(void) { /************************************** * * p a r s e _ a s s i g n m e n t * ************************************** * * Functional description * Parse an assignment statement (or give an error). The * assignment statement can be either a simple assignment * (field = value) or a restructure (relation = rse). * If the assignment operatr is missing, * generate an "expected statement" error. * **************************************/ SYN node, field; QLI_REL relation; NAM name, name2; node = SYNTAX_NODE(nod_assign, s_asn_count); node->syn_arg[s_asn_to] = parse_field_name(&field); name = (NAM) field->syn_arg[0]; /* If the next token is an equals sign, the statement really is an assignment, and we're off the hook. */ if (!MATCH(KW_EQUALS)) ERRQ_print_error(156, name->nam_string, NULL, NULL, NULL, NULL); /* Msg156 expected statement, encountered %s */ /* See if the "field name" is really a relation reference. If so, turn the assignment into a restructure. */ if (field->syn_count == 1) relation = resolve_relation(0, name->nam_symbol); else if (field->syn_count == 2 && name->nam_symbol) { name2 = (NAM) field->syn_arg[1]; relation = resolve_relation(name->nam_symbol, name2->nam_symbol); } else relation = NULL; if (relation) { ALL_release((FRB) field); node->syn_type = nod_restructure; node->syn_arg[s_asn_to] = field = SYNTAX_NODE(nod_relation, s_rel_count); field->syn_arg[s_rel_relation] = (SYN) relation; node->syn_arg[s_asn_from] = parse_rse(); } else node->syn_arg[s_asn_from] = parse_value(0, 0); return node; } static SYN parse_boolean( USHORT * paren_count) { /************************************** * * p a r s e _ b o o l e a n * ************************************** * * Functional description * Parse a general boolean expression. By precedence, handle an OR * here. * **************************************/ SYN expr, node; USHORT local_count; if (!paren_count) { local_count = 0; paren_count = &local_count; } expr = parse_and(paren_count); /* while (*paren_count && KEYWORD (KW_RIGHT_PAREN)) { parse_matching_paren(); (*paren_count)--; } */ if (!MATCH(KW_OR)) { parse_terminating_parens(paren_count, &local_count); return expr; } node = SYNTAX_NODE(nod_or, 2); node->syn_arg[0] = expr; node->syn_arg[1] = parse_boolean(paren_count); parse_terminating_parens(paren_count, &local_count); return node; } static SYN parse_copy(void) { /************************************** * * p a r s e _ c o p y * ************************************** * * Functional description * Parse a copy command, which copies * one procedure to another. * **************************************/ SYN node; PAR_real_token(); if (MATCH(KW_PROCEDURE)) { node = SYNTAX_NODE(nod_copy_proc, 2); node->syn_arg[0] = (SYN) parse_qualified_procedure(); MATCH(KW_TO); node->syn_arg[1] = (SYN) parse_qualified_procedure(); return node; } ERRQ_print_error(157, QLI_token->tok_string, NULL, NULL, NULL, NULL); /* Msg157 Expected PROCEDURE encountered %s */ return NULL; } static DBB parse_database(void) { /************************************** * * p a r s e _ d a t a b a s e * ************************************** * * Functional description * Pick up a database for a meta-data or * procedure update. Return NULL if the * token is not a database name. * **************************************/ DBB database; SYM db_symbol; if ((db_symbol = QLI_token->tok_symbol) && db_symbol->sym_type == SYM_database) { database = (DBB) db_symbol->sym_object; PAR_real_token(); if (!MATCH(KW_DOT)) SYNTAX_ERROR(158); // Msg158 period in qualified name PAR_real(); return database; } if (!QLI_databases) IBERROR(159); // Msg159 no databases are ready return NULL; } static SYN parse_declare(void) { /************************************** * * p a r s e _ d e c l a r e * ************************************** * * Functional description * Parse a variable declaration. * **************************************/ SYN node, field_node; SYM name, query_name; QLI_FLD global_variable; USHORT dtype, length, scale; SSHORT sub_type, sub_type_missing; TEXT *edit_string, *query_header; QLI_REL relation; NAM db_name, rel_name; PAR_token(); PAR_real(); dtype = length = scale = 0; sub_type = 0; sub_type_missing = 1; field_node = NULL; relation = NULL; query_name = NULL; edit_string = query_header = NULL; name = parse_symbol(); /*if (global_flag) PAR_real();*/ while (!KEYWORD(KW_SEMI) && !KEYWORD(KW_COMMA)) { PAR_real(); switch (QLI_token->tok_keyword) { case KW_SHORT: case KW_LONG: case KW_FLOAT: case KW_DOUBLE: case KW_DATE: case KW_CHAR: case KW_VARYING: if (dtype) SYNTAX_ERROR(164); // Msg164 variable definition clause dtype = parse_dtype(&length, &scale); break; case KW_BLOB: IBERROR(160); // Msg160 blob variables are not supported break; case KW_SUB_TYPE: sub_type = parse_dtype_subtype(); sub_type_missing = 0; break; case KW_EDIT_STRING: PAR_token(); if (QLI_token->tok_type != tok_quoted) SYNTAX_ERROR(163); // Msg163 quoted edit string edit_string = make_string(QLI_token->tok_string + 1, QLI_token->tok_length - 2); PAR_token(); break; case KW_QUERY_NAME: PAR_token(); MATCH(KW_IS); if (QLI_token->tok_type != tok_ident) SYNTAX_ERROR(199); // Msg199 identifier query_name = parse_symbol(); break; case KW_QUERY_HEADER: PAR_token(); query_header = parse_header(); break; case KW_BASED: PAR_token(); MATCH(KW_ON); field_node = parse_field_name(0); break; default: SYNTAX_ERROR(164); // Msg164 variable definition clause break; } } if (field_node && field_node->syn_count == 3) { db_name = (NAM) field_node->syn_arg[0]; rel_name = (NAM) field_node->syn_arg[1]; if (!db_name->nam_symbol) ERRQ_print_error(165, db_name->nam_string, NULL, NULL, NULL, NULL); /* Msg165 %s is not a database */ if (! (relation = resolve_relation(db_name->nam_symbol, rel_name->nam_symbol))) ERRQ_print_error(166, rel_name-> nam_string, db_name-> nam_string, NULL, NULL, NULL); /* Msg166 %s is not a relation in database %s */ } if (!dtype && !field_node) SYNTAX_ERROR(167); // Msg167 variable data type if (field_node && (dtype || length || scale)) IBERROR(168); // Msg168 no datatype may be specified for a variable based on a field node = SYNTAX_NODE(nod_declare, 2); global_variable = (QLI_FLD) ALLOCDV(type_fld, length); node->syn_arg[0] = (SYN) global_variable; global_variable->fld_name = name; global_variable->fld_dtype = dtype; global_variable->fld_scale = scale; global_variable->fld_sub_type = sub_type; global_variable->fld_sub_type_missing = sub_type_missing; global_variable->fld_length = length; global_variable->fld_edit_string = edit_string; global_variable->fld_query_name = query_name; global_variable->fld_query_header = query_header; global_variable->fld_relation = relation; node->syn_arg[1] = field_node; check_end(); return node; } static SYN parse_define(void) { /************************************** * * p a r s e _ d e f i n e * ************************************** * * Functional description * Parse a DEFINE command. * There are, of course, a whole class of define commands. * **************************************/ SYN node; PAR_real_token(); if (MATCH(KW_PROCEDURE)) { PAR_real(); node = SYNTAX_NODE(nod_define, 1); node->syn_arg[0] = (SYN) parse_qualified_procedure(); return node; } if (MATCH(KW_FIELD)) { PAR_real(); node = SYNTAX_NODE(nod_def_field, 2); node->syn_arg[0] = (SYN) parse_database(); node->syn_arg[1] = (SYN) parse_field(TRUE); return node; } if (MATCH(KW_RELATION)) return parse_def_relation(); if (KEYWORD(KW_DATABASE)) return parse_ready(nod_def_database); if (MATCH(KW_INDEX)) return parse_def_index(); SYNTAX_ERROR(169); // Msg169 object type for DEFINE return NULL; } static SYN parse_def_index(void) { /************************************** * * p a r s e _ d e f _ i n d e x * ************************************** * * Functional description * Parse a DEFINE INDEX command. * **************************************/ LLS stack; SYN node; PAR_real(); node = SYNTAX_NODE(nod_def_index, s_dfi_count); node->syn_arg[s_dfi_name] = (SYN) parse_symbol(); PAR_real(); MATCH(KW_FOR); if (!(node->syn_arg[s_dfi_relation] = (SYN) parse_qualified_relation())) SYNTAX_ERROR(170); // Msg170 relation name PAR_real(); while (true) { PAR_real(); if (MATCH(KW_UNIQUE)) node->syn_flags |= s_dfi_flag_unique; else if (MATCH(KW_DUPLICATE)) node->syn_flags &= ~s_dfi_flag_unique; else if (MATCH(KW_ACTIVE)) node->syn_flags &= ~s_dfi_flag_inactive; else if (MATCH(KW_INACTIVE)) node->syn_flags |= s_dfi_flag_inactive; else if (MATCH(KW_DESCENDING)) node->syn_flags |= s_dfi_flag_descending; else if (MATCH(KW_ASCENDING)) node->syn_flags &= ~s_dfi_flag_descending; else break; } stack = NULL; for (;;) { LLS_PUSH(parse_name(), &stack); if (!MATCH(KW_COMMA)) break; } node->syn_arg[s_dfi_fields] = make_list(stack); command_end(); return node; } static SYN parse_def_relation(void) { /************************************** * * p a r s e _ d e f _ r e l a t i o n * ************************************** * * Functional description * Parse a DEFINE RELATION command, * which include the field definitions * for a primitive relation definition * or it may just reference another relatio * whose field definitions we will copy. * **************************************/ QLI_REL relation; QLI_FLD field, *ptr; SYN node; PAR_real(); node = SYNTAX_NODE(nod_def_relation, 2); relation = (QLI_REL) ALLOCD(type_rel); node->syn_arg[0] = (SYN) relation; relation->rel_database = parse_database(); relation->rel_symbol = parse_symbol(); PAR_real(); if (MATCH(KW_BASED)) { PAR_real(); MATCH(KW_ON); PAR_real(); MATCH(KW_RELATION); PAR_real(); relation = (QLI_REL) ALLOCD(type_rel); node->syn_arg[1] = (SYN) relation; relation->rel_database = parse_database(); relation->rel_symbol = parse_symbol(); } else { node->syn_arg[1] = NULL; ptr = &relation->rel_fields; for (;;) { MATCH(KW_ADD); PAR_real(); MATCH(KW_FIELD); *ptr = field = parse_field(FALSE); ptr = &field->fld_next; if (KEYWORD(KW_SEMI)) break; if (!MATCH(KW_COMMA)) SYNTAX_ERROR(171); // Msg171 comma between field definitions } } command_end(); return node; } static SYN parse_delete(void) { /************************************** * * p a r s e _ d e l e t e * ************************************** * * Functional description * Parse a SQL DELETE statement. * (DELETE PROCEDURE is parsed in parse_drop) * **************************************/ SYN node, rse; ++sql_flag; if (!MATCH(KW_FROM)) SYNTAX_ERROR(172); // Msg172 FROM node = SYNTAX_NODE(nod_erase, s_era_count); node->syn_arg[s_era_rse] = rse = SYNTAX_NODE(nod_rse, (int) s_rse_count + 2); rse->syn_count = 1; rse->syn_arg[s_rse_count] = parse_sql_relation(); /* Pick up boolean, if present */ if (MATCH(KW_WITH)) rse->syn_arg[s_rse_boolean] = parse_boolean(0); --sql_flag; return node; } static SYN parse_drop(void) { /************************************** * * p a r s e _ d r o p * ************************************** * * Functional description * Parse a DDL DROP/DELETE command. It it isn't one, * just return NULL. * **************************************/ SYN node; NOD_T type; DBB database; SSHORT l; TEXT *p, *q; PAR_real_token(); if (MATCH(KW_RELATION) || MATCH(KW_VIEW) || MATCH(KW_TABLE)) { node = SYNTAX_NODE(nod_del_relation, 1); if (!(node->syn_arg[0] = (SYN) parse_qualified_relation())) SYNTAX_ERROR(173); // Msg173 relation or view name return node; } switch (QLI_token->tok_keyword) { case KW_PROCEDURE: type = nod_delete_proc; break; case KW_INDEX: type = nod_del_index; break; case KW_FIELD: type = nod_del_field; break; case KW_DATABASE: LEX_filename(); if (!(l = QLI_token->tok_length)) ERRQ_error(429, NULL, NULL, NULL, NULL, NULL); // Msg429 database file name required on DROP DATABASE q = QLI_token->tok_string; if (QLI_token->tok_type == tok_quoted) { l -= 2; q++; } database = (DBB) ALLOCDV(type_dbb, l); database->dbb_filename_length = l; p = database->dbb_filename; do *p++ = *q++; while (--l); PAR_token(); // parse an optional user name and password if given for (;;) { if (MATCH(KW_USER)) database->dbb_user = parse_literal(); else if (MATCH(KW_PASSWORD)) database->dbb_password = parse_literal(); else break; } command_end(); node = SYNTAX_NODE(nod_del_database, 1); node->syn_arg[0] = (SYN) database; return node; default: return NULL; } PAR_real_token(); node = SYNTAX_NODE(type, 2); if (type == nod_delete_proc) node->syn_arg[0] = (SYN) parse_qualified_procedure(); else { node->syn_arg[0] = (SYN) parse_database(); node->syn_arg[1] = (SYN) parse_name(); } return node; } static int parse_dtype( USHORT * length, USHORT * scale) { /************************************** * * p a r s e _ d t y p e * ************************************** * * Functional description * Parse a datatype clause. * **************************************/ KWWORDS keyword; USHORT dtype, l; keyword = QLI_token->tok_keyword; PAR_token(); *scale = 0; switch (keyword) { case KW_SHORT: *length = sizeof(SSHORT); dtype = dtype_short; break; case KW_LONG: *length = sizeof(SLONG); dtype = dtype_long; break; case KW_FLOAT: *length = sizeof(float); return dtype_real; case KW_DOUBLE: *length = sizeof(double); return dtype_double; case KW_DATE: *length = sizeof(gds_quad); return dtype_timestamp; case KW_CHAR: dtype = dtype_text; break; case KW_VARYING: dtype = dtype_varying; break; case KW_BLOB: *length = sizeof(gds_quad); return dtype_blob; } if (dtype == dtype_short || dtype == dtype_long) { if (MATCH(KW_SCALE)) { l = (MATCH(KW_MINUS)) ? TRUE : FALSE; *scale = parse_ordinal(); if (l) *scale = -(*scale); } } else if (dtype == dtype_text || dtype == dtype_varying) { if (!MATCH(KW_L_BRCKET) && !MATCH(KW_LT)) SYNTAX_ERROR(174); /* Msg174 "[" */ l = parse_ordinal(); if (dtype == dtype_varying) l += sizeof(SSHORT); *length = l; if (!MATCH(KW_R_BRCKET) && !MATCH(KW_GT)) SYNTAX_ERROR(175); /* Msg175 "]" */ } return dtype; } static int parse_dtype_subtype(void) { /************************************** * * p a r s e _ d t y p e _ s u b t y p e * ************************************** * * Functional description * Parse a sub-type definition, which can be any of * SUB_TYPE {IS} [TEXT | FIXED | ] * * Returns the numeric subtype value, * **************************************/ int sign; /* grab KW_SUB_TYPE */ PAR_token(); MATCH(KW_IS); if (MATCH(KW_TEXT) || MATCH(KW_FIXED)) return 1; sign = (MATCH(KW_MINUS)) ? -1 : 1; return (sign * parse_ordinal()); } static SYN parse_edit(void) { /************************************** * * p a r s e _ e d i t * ************************************** * * Functional description * Parse an edit statement which can * be any of EDIT * EDIT * EDIT <*> * EDIT * * **************************************/ SYN node; NOD_T type; int l; LLS start, stop, statement_list; LEX_token(); /* * edit previous statements. The top of the statment list * is this edit command, which we conveniently ignore. */ if (KEYWORD(KW_SEMI) || (QLI_token->tok_type == tok_number) || (KEYWORD(KW_ASTERISK))) { if (!(statement_list = LEX_statement_list())) IBERROR(176); // Msg176 No statements issued yet if (MATCH(KW_ASTERISK)) LEX_edit((SLONG) 0, (SLONG) statement_list->lls_object); else { if (KEYWORD(KW_SEMI)) l = 1; else if (QLI_token->tok_type == tok_number) l = parse_ordinal(); for (start = stop = statement_list; l && start->lls_next; l--, start = start->lls_next); command_end(); LEX_edit((SLONG) start->lls_object, (SLONG) stop->lls_object); } } #ifdef PYXIS else if (MATCH(KW_FORM)) IBERROR(484); // FORMs not supported #endif else { type = nod_edit_proc; node = SYNTAX_NODE(type, 2); node->syn_arg[0] = (SYN) parse_qualified_procedure(); command_end(); return node; } return NULL; } static TEXT *parse_edit_string(void) { /************************************** * * p a r s e _ e d i t _ s t r i n g * ************************************** * * Functional description * Look for and parse a clause of the form: * USING * **************************************/ if (!KEYWORD(KW_USING)) return NULL; LEX_edit_string(); return parse_string(); } static SYN parse_erase(void) { /************************************** * * p a r s e _ e r a s e * ************************************** * * Functional description * Parse an ERASE statement. Erase can be any of the * following: * * ERASE [ALL] [OF ] * **************************************/ SYN node; PAR_token(); MATCH(KW_ALL); MATCH(KW_OF); node = SYNTAX_NODE(nod_erase, s_era_count); if (MATCH(KW_ALL) || potential_rse()) { MATCH(KW_OF); node->syn_arg[s_era_rse] = parse_rse(); } return node; } static SYN parse_extract(void) { /************************************** * * p a r s e _ e x t r a c t * ************************************** * * Functional description * Parse a procedure extract statement. Syntax is: * * EXTRACT [ON ] proc [, ...] [ON ] * **************************************/ LLS stack; SYN node; PAR_real_token(); node = SYNTAX_NODE(nod_extract, 2); node->syn_arg[1] = parse_output(); if (!MATCH(KW_ALL)) { stack = NULL; for (;;) { LLS_PUSH(parse_qualified_procedure(), &stack); if (!MATCH(KW_COMMA)) break; } node->syn_arg[0] = make_list(stack); } if (!node->syn_arg[1] && !(node->syn_arg[1] = parse_output())) SYNTAX_ERROR(177); /* Msg177 "ON or TO" */ return node; } static QLI_FLD parse_field( int global_flag) { /************************************** * * p a r s e _ f i e l d * ************************************** * * Functional description * Parse a field description. * **************************************/ QLI_FLD field; SYM name, query_name, based_on; USHORT dtype, length, scale; SSHORT sub_type, sub_type_missing; TEXT *edit_string, *query_header; PAR_real(); dtype = length = scale = 0; sub_type = 0; sub_type_missing = 1; query_name = based_on = NULL; edit_string = query_header = NULL; name = parse_symbol(); if (global_flag) PAR_real(); while (!KEYWORD(KW_SEMI) && !KEYWORD(KW_COMMA)) { PAR_real(); switch (QLI_token->tok_keyword) { case KW_SHORT: case KW_LONG: case KW_FLOAT: case KW_DOUBLE: case KW_DATE: case KW_CHAR: case KW_VARYING: case KW_BLOB: if (dtype) SYNTAX_ERROR(179); // Msg179 field definition clause dtype = parse_dtype(&length, &scale); break; case KW_SUB_TYPE: sub_type = parse_dtype_subtype(); sub_type_missing = 0; break; case KW_EDIT_STRING: PAR_token(); if (QLI_token->tok_type != tok_quoted) SYNTAX_ERROR(178); // Msg178 quoted edit string edit_string = make_string(QLI_token->tok_string + 1, QLI_token->tok_length - 2); PAR_token(); break; case KW_QUERY_NAME: PAR_token(); MATCH(KW_IS); if (QLI_token->tok_type != tok_ident) SYNTAX_ERROR(199); // Msg199 identifier query_name = parse_symbol(); break; case KW_BASED: PAR_token(); MATCH(KW_ON); based_on = parse_symbol(); break; default: SYNTAX_ERROR(179); // Msg179 field definition clause break; } } field = (QLI_FLD) ALLOCDV(type_fld, length); field->fld_name = name; field->fld_dtype = dtype; field->fld_scale = scale; field->fld_sub_type = sub_type; field->fld_sub_type_missing = sub_type_missing; field->fld_length = length; field->fld_edit_string = edit_string; field->fld_query_name = query_name; field->fld_query_header = query_header; if (!global_flag) field->fld_based = based_on; else if (based_on) IBERROR(180); // Msg180 global fields may not be based on other fields return field; } static SYN parse_field_name( SYN * fld_ptr) { /************************************** * * p a r s e _ f i e l d _ n a m e * ************************************** * * Functional description * Parse a qualified field name, or * qualified * expression. * **************************************/ SYN field, node; LLS stack; stack = NULL; while (true) { if (MATCH(KW_ASTERISK)) { if (!stack) SYNTAX_ERROR(181); // Msg181 field name or asterisk expression field = make_list(stack); field->syn_type = nod_star; return field; } LLS_PUSH(parse_name(), &stack); if (!MATCH(KW_DOT)) break; } field = make_list(stack); field->syn_type = nod_field; if (fld_ptr) *fld_ptr = field; if (!(MATCH(KW_L_BRCKET))) return field; // Parse an array reference stack = NULL; for (;;) { LLS_PUSH(parse_value(0, 0), &stack); if (MATCH(KW_R_BRCKET)) break; if (!MATCH(KW_COMMA)) SYNTAX_ERROR(183); // Msg183 comma } node = SYNTAX_NODE(nod_index, s_idx_count); node->syn_arg[s_idx_field] = field; node->syn_arg[s_idx_subs] = make_list(stack); return node; } static SYN parse_for(void) { /************************************** * * p a r s e _ f o r * ************************************** * * Functional description * Parse a FOR statement. * **************************************/ SYN node; PAR_token(); #ifdef PYXIS if (MATCH(KW_FORM)) IBERROR(484); // FORMs not supported #endif node = SYNTAX_NODE(nod_for, s_for_count); node->syn_arg[s_for_rse] = parse_rse(); node->syn_arg[s_for_statement] = parse_statement(); return node; } #ifdef PYXIS static SYN parse_form(void) { /************************************** * * p a r s e _ f o r m * ************************************** * * Functional description * Parse qualified form name, returning a form block. * **************************************/ IBERROR(484); // FORMs not supported } #endif static SYN parse_from( USHORT * paren_count, USHORT * bool_flag) { /************************************** * * p a r s e _ f r o m * ************************************** * * Functional description * Parse either an explicit or implicit FIRST ... FROM statement. * **************************************/ SYN node, value; PAR_real(); if (MATCH(KW_FIRST)) { value = parse_primitive_value(0, 0); PAR_real(); if (!MATCH(KW_FROM)) SYNTAX_ERROR(182); // Msg182 FROM rse clause } else { value = parse_primitive_value(paren_count, bool_flag); if (sql_flag || !MATCH(KW_FROM)) return value; } node = SYNTAX_NODE(nod_from, s_stt_count); node->syn_arg[s_stt_value] = value; node->syn_arg[s_stt_rse] = parse_rse(); if (MATCH(KW_ELSE)) node->syn_arg[s_stt_default] = parse_value(0, 0); return node; } static SYN parse_function(void) { /************************************** * * p a r s e _ f u n c t i o n * ************************************** * * Functional description * Parse a function reference. * **************************************/ SYN node; LLS stack; function_count++; node = SYNTAX_NODE(nod_function, s_fun_count); node->syn_arg[s_fun_function] = (SYN) QLI_token->tok_symbol; node->syn_count = 1; PAR_token(); stack = NULL; if (MATCH(KW_LEFT_PAREN)) for (;;) { LLS_PUSH(parse_value(0, 0), &stack); if (MATCH(KW_RIGHT_PAREN)) break; if (!MATCH(KW_COMMA)) SYNTAX_ERROR(183); // Msg183 comma } node->syn_arg[s_fun_args] = make_list(stack); function_count--; return node; } static TEXT *parse_header(void) { /************************************** * * p a r s e _ h e a d e r * ************************************** * * Functional description * Parse and store headers of the form: * "quoted_string" [/ "more_string"]... * or even the non-header - * **************************************/ TEXT *p, *q, header[1024]; p = header; while (true) { PAR_real(); if ((QLI_token->tok_keyword != KW_MINUS) && (QLI_token->tok_type != tok_quoted)) SYNTAX_ERROR(184); // Msg184 quoted header segment q = QLI_token->tok_string; while (*q) *p++ = *q++; PAR_real_token(); if (!MATCH(KW_SLASH)) break; } return make_string(header, p - header); } static SYN parse_help(void) { /************************************** * * p a r s e _ h e l p * ************************************** * * Functional description * Parse HELP statement. Unreasonable, but the masses * must be appeased. Bread, circuses, help. * **************************************/ SYN node; LLS stack; stack = NULL; PAR_token(); while (!KEYWORD(KW_SEMI)) { LLS_PUSH(parse_name(), &stack); MATCH(KW_COMMA); } node = make_list(stack); node->syn_type = nod_help; command_end(); return node; } static SYN parse_if(void) { /************************************** * * p a r s e _ i f * ************************************** * * Functional description * Parse an IF THEN ELSE statement. * **************************************/ SYN node; PAR_token(); node = SYNTAX_NODE(nod_if, s_if_count); node->syn_arg[s_if_boolean] = parse_boolean(0); PAR_real(); MATCH(KW_THEN); ++else_count; node->syn_arg[s_if_true] = parse_statement(); --else_count; if (MATCH(KW_ELSE)) node->syn_arg[s_if_false] = parse_statement(); return node; } static SYN parse_in( SYN value, NOD_T operatr, USHORT all_flag) { /************************************** * * p a r s e _ i n * ************************************** * * Functional description * Parse a SQL "IN" clause. This can have two forms: * * value IN (exp1, exp2...) * * value IN (column