/* * PROGRAM: JRD Command Oriented Query Language * MODULE: parse.cpp * 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 #include #include #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" #include "../jrd/gdsassert.h" #include "../jrd/constants.h" using MsgFormat::SafeArg; #define KEYWORD(kw) (QLI_token->tok_keyword == kw) #define INT_CAST (qli_syntax*) (IPTR) static void check_end(void); static void command_end(void); static DBB get_dbb(qli_symbol*); static qli_syntax* make_list(qli_lls*); static NAM make_name(void); static qli_const* make_numeric_constant(const TEXT*, USHORT); static TEXT* make_string(const TEXT*, USHORT); static qli_syntax* negate(qli_syntax*); static KWWORDS next_keyword(void); static qli_syntax* parse_abort(void); static qli_syntax* parse_accept(void); static qli_syntax* parse_add(USHORT *, bool *); static qli_syntax* parse_and(USHORT *); static qli_syntax* parse_assignment(void); static qli_syntax* parse_boolean(USHORT *); static qli_syntax* parse_copy(void); static DBB parse_database(void); static qli_syntax* parse_declare(void); static qli_syntax* parse_define(void); static qli_syntax* parse_def_index(void); static qli_syntax* parse_def_relation(void); static qli_syntax* parse_delete(void); static qli_syntax* parse_drop(void); static int parse_dtype(USHORT *, USHORT *); static int parse_dtype_subtype(void); static qli_syntax* parse_edit(void); static TEXT* parse_edit_string(void); static qli_syntax* parse_erase(void); static qli_syntax* parse_extract(void); static qli_fld* parse_field(bool); static qli_syntax* parse_field_name(qli_syntax* *); static qli_syntax* parse_for(void); static qli_syntax* parse_from(USHORT*, bool*); static qli_syntax* parse_function(void); static TEXT* parse_header(void); static qli_syntax* parse_help(void); static qli_syntax* parse_if(void); static qli_syntax* parse_in(qli_syntax*, NOD_T, bool); static qli_syntax* parse_insert(void); static NOD_T parse_join_type(void); static qli_syntax* parse_list_fields(void); static qli_const* parse_literal(void); static qli_syntax* parse_matches(void); static void parse_matching_paren(void); static qli_syntax* parse_modify(void); static qli_syntax* parse_modify_index(void); static qli_syntax* parse_modify_relation(void); static qli_syntax* parse_multiply(USHORT*, bool*); static NAM parse_name(void); static qli_syntax* parse_not(USHORT *); static int parse_ordinal(void); static qli_syntax* parse_output(void); static qli_syntax* parse_primitive_value(USHORT*, bool*); static qli_syntax* parse_print_list(void); static qli_syntax* parse_print(void); static qli_syntax* 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 qli_syntax* parse_ready(NOD_T); static qli_syntax* parse_relational(USHORT*); static qli_syntax* parse_relation(void); static qli_syntax* parse_rename(void); static qli_syntax* parse_repeat(void); static qli_syntax* parse_report(void); static qli_syntax* parse_rse(void); static qli_syntax* parse_select(void); static qli_syntax* parse_set(void); static qli_syntax* parse_shell(void); static qli_syntax* parse_show(void); static qli_syntax* parse_sort(void); static qli_syntax* parse_sql_alter(void); static qli_syntax* parse_sql_create(void); static int parse_sql_dtype(USHORT* length, USHORT* scale, USHORT* precision, USHORT* sub_type); static qli_fld* parse_sql_field(void); static qli_syntax* parse_sql_grant_revoke(USHORT); static qli_syntax* parse_sql_index_create(const bool, const bool); static qli_syntax* parse_sql_joined_relation(qli_syntax*); static qli_syntax* parse_sql_join_clause(qli_syntax*); static qli_syntax* parse_sql_table_create(void); #ifdef NOT_USED_OR_REPLACED static qli_syntax* parse_sql_view_create(void); #endif static qli_syntax* parse_sql_relation(void); static qli_syntax* parse_sql_rse(void); static qli_syntax* parse_sql_singleton_select(void); static qli_syntax* parse_sql_subquery(void); static qli_syntax* parse_statement(void); static qli_syntax* parse_statistical(void); static qli_syntax* parse_store(void); static TEXT *parse_string(void); static qli_symbol* parse_symbol(void); static void parse_terminating_parens(USHORT*, USHORT*); static qli_syntax* parse_transaction(NOD_T); static qli_syntax* parse_udf_or_field(void); static qli_syntax* parse_update(void); static qli_syntax* parse_value(USHORT*, bool*); static bool potential_rse(void); static qli_rel* resolve_relation(qli_symbol*, qli_symbol*); static qli_syntax* syntax_node(NOD_T, USHORT); static bool 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; static bool sw_statement, sw_sql_view; static SSHORT function_count; // indicates the depth of UDF calls struct nod_types { KWWORDS nod_t_keyword; NOD_T nod_t_node; NOD_T nod_t_rpt_node; NOD_T nod_t_sql_node; }; static const nod_types 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 const 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 }; qli_syntax* PARQ_parse(void) { /************************************** * * P A R Q _ p a r s e * ************************************** * * Functional description * Parse a single statement or command. * **************************************/ 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: { qli_syntax* node = parse_drop(); if (node) return node; node = parse_delete(); check_end(); if (!PAR_match(KW_THEN)) return node; qli_lls* stack = NULL; ALLQ_push((blk*) node, &stack); ALLQ_push((blk*) 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(); } bool 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. * **************************************/ if (KEYWORD(keyword)) { PAR_token(); return true; } for (const qli_symbol* 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. * **************************************/ while ((QLI_token->tok_type == tok_eol) || KEYWORD(KW_continuation)) LEX_token(); if (PAR_match(KW_COLON)) { DBB 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. * **************************************/ for (;;) { LEX_token(); if (!(KEYWORD(KW_continuation)) && !(sw_statement && QLI_semi && QLI_token->tok_type == tok_eol)) { break; } } if (PAR_match(KW_COLON)) { if (!QLI_databases) { ERRQ_error_format(159); // Msg159 no databases are ready ERRQ_pending(); LEX_token(); } else { DBB 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; } ERRQ_syntax(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; } ERRQ_syntax(162); // Msg162 end of command } static DBB get_dbb( qli_symbol* 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 qli_syntax* make_list( qli_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. * **************************************/ qli_lls* temp = stack; USHORT count = 0; while (temp) { count++; temp = temp->lls_next; } qli_syntax* node = syntax_node(nod_list, count); qli_syntax** ptr = &node->syn_arg[count]; while (stack) *--ptr = (qli_syntax*) ALLQ_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. * **************************************/ SSHORT l; TEXT string[32]; for (SSHORT 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; } NAM name = (NAM) ALLOCDV(type_nam, l); name->nam_length = l; TEXT* p = name->nam_string; const TEXT* q = string; if (l) do { const TEXT c = *q++; *p++ = UPPER(c); } while (--l); return name; } static qli_const* make_numeric_constant(const 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. * **************************************/ qli_const* constant; // If there are a reasonable number of digits, convert to binary if (length < 9) { constant = (qli_const*) 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 = (qli_const*) 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; TEXT* p = (TEXT *) constant->con_desc.dsc_address; *p++ = '0'; memcpy(p, string, --length); } return constant; } static TEXT* make_string(const TEXT* address, USHORT length) { /************************************** * * m a k e _ s t r i n g * ************************************** * * Functional description * Copy a string into a temporary string block. * **************************************/ qli_str* string = (qli_str*) ALLOCDV(type_str, length); if (length) memcpy(string->str_data, address, length); return string->str_data; } static qli_syntax* negate( qli_syntax* expr) { /************************************** * * n e g a t e * ************************************** * * Functional description * Build negation of expression. * **************************************/ qli_syntax* 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. * **************************************/ PAR_real(); for (const qli_symbol* 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 qli_syntax* parse_abort(void) { /************************************** * * p a r s e _ a b o r t * ************************************** * * Functional description * Parse an ABORT statement. * **************************************/ PAR_token(); qli_syntax* 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 qli_syntax* 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 qli_syntax* parse_add( USHORT* paren_count, bool* bool_flag) { /************************************** * * p a r s e _ a d d * ************************************** * * Functional description * Parse the lowest precedence operatrs, ADD and SUBTRACT. * **************************************/ NOD_T operatr; qli_syntax* node = parse_multiply(paren_count, bool_flag); while (true) { if (PAR_match(KW_PLUS)) operatr = nod_add; else if (PAR_match(KW_MINUS)) operatr = nod_subtract; else return node; qli_syntax* arg = node; node = syntax_node(operatr, 2); node->syn_arg[0] = arg; node->syn_arg[1] = parse_multiply(paren_count, bool_flag); } } static qli_syntax* parse_and( USHORT * paren_count) { /************************************** * * p a r s e _ a n d * ************************************** * * Functional description * Parse an AND expression. * **************************************/ qli_syntax* expr = parse_not(paren_count); /* while (*paren_count && KEYWORD (KW_RIGHT_PAREN)) { parse_matching_paren(); (*paren_count)--; } */ if (!PAR_match(KW_AND)) return expr; qli_syntax* node = syntax_node(nod_and, 2); node->syn_arg[0] = expr; node->syn_arg[1] = parse_and(paren_count); return node; } static qli_syntax* 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 operator is missing, * generate an "expected statement" error. * **************************************/ qli_syntax* field; qli_syntax* node = syntax_node(nod_assign, s_asn_count); node->syn_arg[s_asn_to] = parse_field_name(&field); NAM 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 (!PAR_match(KW_EQUALS)) ERRQ_print_error(156, name->nam_string); // Msg156 expected statement, encountered %s /* See if the "field name" is really a relation reference. If so, turn the assignment into a restructure. */ qli_rel* relation = NULL; if (field->syn_count == 1) relation = resolve_relation(0, name->nam_symbol); else if (field->syn_count == 2 && name->nam_symbol) { NAM name2 = (NAM) field->syn_arg[1]; relation = resolve_relation(name->nam_symbol, name2->nam_symbol); } if (relation) { ALLQ_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] = (qli_syntax*) relation; node->syn_arg[s_asn_from] = parse_rse(); } else node->syn_arg[s_asn_from] = parse_value(0, 0); return node; } static qli_syntax* 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. * **************************************/ USHORT local_count; if (!paren_count) { local_count = 0; paren_count = &local_count; } qli_syntax* expr = parse_and(paren_count); /* while (*paren_count && KEYWORD (KW_RIGHT_PAREN)) { parse_matching_paren(); (*paren_count)--; } */ if (!PAR_match(KW_OR)) { parse_terminating_parens(paren_count, &local_count); return expr; } qli_syntax* 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 qli_syntax* parse_copy(void) { /************************************** * * p a r s e _ c o p y * ************************************** * * Functional description * Parse a copy command, which copies * one procedure to another. * **************************************/ PAR_real_token(); if (PAR_match(KW_PROCEDURE)) { qli_syntax* node = syntax_node(nod_copy_proc, 2); node->syn_arg[0] = (qli_syntax*) parse_qualified_procedure(); PAR_match(KW_TO); node->syn_arg[1] = (qli_syntax*) parse_qualified_procedure(); return node; } ERRQ_print_error(157, QLI_token->tok_string); // 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. * **************************************/ qli_symbol* db_symbol = QLI_token->tok_symbol; if (db_symbol && db_symbol->sym_type == SYM_database) { DBB database = (DBB) db_symbol->sym_object; PAR_real_token(); if (!PAR_match(KW_DOT)) ERRQ_syntax(158); // Msg158 period in qualified name PAR_real(); return database; } if (!QLI_databases) IBERROR(159); // Msg159 no databases are ready return NULL; } static qli_syntax* parse_declare(void) { /************************************** * * p a r s e _ d e c l a r e * ************************************** * * Functional description * Parse a variable declaration. * **************************************/ PAR_token(); PAR_real(); USHORT dtype = 0, length = 0, scale = 0; SSHORT sub_type = 0; SSHORT sub_type_missing = 1; qli_syntax* field_node = NULL; qli_symbol* query_name = NULL; const TEXT* edit_string = NULL; const TEXT* query_header = NULL; qli_symbol* 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) ERRQ_syntax(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) ERRQ_syntax(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(); PAR_match(KW_IS); if (QLI_token->tok_type != tok_ident) ERRQ_syntax(199); // Msg199 identifier query_name = parse_symbol(); break; case KW_QUERY_HEADER: PAR_token(); query_header = parse_header(); break; case KW_BASED: PAR_token(); PAR_match(KW_ON); field_node = parse_field_name(0); break; default: ERRQ_syntax(164); // Msg164 variable definition clause break; } } qli_rel* relation = NULL; if (field_node && field_node->syn_count == 3) { NAM db_name = (NAM) field_node->syn_arg[0]; NAM rel_name = (NAM) field_node->syn_arg[1]; if (!db_name->nam_symbol) ERRQ_print_error(165, db_name->nam_string); // Msg165 %s is not a database relation = resolve_relation(db_name->nam_symbol, rel_name->nam_symbol); if (!relation) { ERRQ_print_error(166, SafeArg() << rel_name->nam_string << db_name->nam_string); // Msg166 %s is not a relation in database %s } } if (!dtype && !field_node) ERRQ_syntax(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 qli_syntax* node = syntax_node(nod_declare, 2); // Not global to this unit, misleading name "global..." qli_fld* global_variable = (qli_fld*) ALLOCDV(type_fld, length); node->syn_arg[0] = (qli_syntax*) 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 qli_syntax* 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. * **************************************/ PAR_real_token(); if (PAR_match(KW_PROCEDURE)) { PAR_real(); qli_syntax* anode = syntax_node(nod_define, 1); anode->syn_arg[0] = (qli_syntax*) parse_qualified_procedure(); return anode; } if (PAR_match(KW_FIELD)) { PAR_real(); qli_syntax* node = syntax_node(nod_def_field, 2); node->syn_arg[0] = (qli_syntax*) parse_database(); node->syn_arg[1] = (qli_syntax*) parse_field(true); return node; } if (PAR_match(KW_RELATION)) return parse_def_relation(); if (KEYWORD(KW_DATABASE)) return parse_ready(nod_def_database); if (PAR_match(KW_INDEX)) return parse_def_index(); ERRQ_syntax(169); // Msg169 object type for DEFINE return NULL; } static qli_syntax* parse_def_index(void) { /************************************** * * p a r s e _ d e f _ i n d e x * ************************************** * * Functional description * Parse a DEFINE INDEX command. * **************************************/ PAR_real(); qli_syntax* node = syntax_node(nod_def_index, s_dfi_count); node->syn_arg[s_dfi_name] = (qli_syntax*) parse_symbol(); PAR_real(); PAR_match(KW_FOR); if (!(node->syn_arg[s_dfi_relation] = (qli_syntax*) parse_qualified_relation())) ERRQ_syntax(170); // Msg170 relation name PAR_real(); while (true) { PAR_real(); if (PAR_match(KW_UNIQUE)) node->syn_flags |= s_dfi_flag_unique; else if (PAR_match(KW_DUPLICATE)) node->syn_flags &= ~s_dfi_flag_unique; else if (PAR_match(KW_ACTIVE)) node->syn_flags &= ~s_dfi_flag_inactive; else if (PAR_match(KW_INACTIVE)) node->syn_flags |= s_dfi_flag_inactive; else if (PAR_match(KW_DESCENDING)) node->syn_flags |= s_dfi_flag_descending; else if (PAR_match(KW_ASCENDING)) node->syn_flags &= ~s_dfi_flag_descending; else break; } qli_lls* stack = NULL; for (;;) { ALLQ_push((blk*) parse_name(), &stack); if (!PAR_match(KW_COMMA)) break; } node->syn_arg[s_dfi_fields] = make_list(stack); command_end(); return node; } static qli_syntax* 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 relation * whose field definitions we will copy. * **************************************/ PAR_real(); qli_syntax* node = syntax_node(nod_def_relation, 2); qli_rel* relation = (qli_rel*) ALLOCD(type_rel); node->syn_arg[0] = (qli_syntax*) relation; relation->rel_database = parse_database(); relation->rel_symbol = parse_symbol(); PAR_real(); if (PAR_match(KW_BASED)) { PAR_real(); PAR_match(KW_ON); PAR_real(); PAR_match(KW_RELATION); PAR_real(); relation = (qli_rel*) ALLOCD(type_rel); node->syn_arg[1] = (qli_syntax*) relation; relation->rel_database = parse_database(); relation->rel_symbol = parse_symbol(); } else { node->syn_arg[1] = NULL; qli_fld** ptr = &relation->rel_fields; for (;;) { PAR_match(KW_ADD); PAR_real(); PAR_match(KW_FIELD); qli_fld* field = parse_field(false); *ptr = field; ptr = &field->fld_next; if (KEYWORD(KW_SEMI)) break; if (!PAR_match(KW_COMMA)) ERRQ_syntax(171); // Msg171 comma between field definitions } } command_end(); return node; } static qli_syntax* 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) * **************************************/ ++sql_flag; if (!PAR_match(KW_FROM)) ERRQ_syntax(172); // Msg172 FROM qli_syntax* node = syntax_node(nod_erase, s_era_count); qli_syntax* rse = syntax_node(nod_rse, (int) s_rse_count + 2); node->syn_arg[s_era_rse] = rse; rse->syn_count = 1; rse->syn_arg[s_rse_count] = parse_sql_relation(); // Pick up boolean, if present if (PAR_match(KW_WITH)) rse->syn_arg[s_rse_boolean] = parse_boolean(0); --sql_flag; return node; } static qli_syntax* 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. * **************************************/ qli_syntax* node; NOD_T type; DBB database; SSHORT l; const TEXT* q; PAR_real_token(); if (PAR_match(KW_RELATION) || PAR_match(KW_VIEW) || PAR_match(KW_TABLE)) { node = syntax_node(nod_del_relation, 1); if (!(node->syn_arg[0] = (qli_syntax*) parse_qualified_relation())) ERRQ_syntax(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); // 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; memcpy(database->dbb_filename, q, l); PAR_token(); // parse an optional user name and password if given for (;;) { if (PAR_match(KW_USER)) database->dbb_user = parse_literal(); else if (PAR_match(KW_PASSWORD)) database->dbb_password = parse_literal(); else break; } command_end(); node = syntax_node(nod_del_database, 1); node->syn_arg[0] = (qli_syntax*) database; return node; default: return NULL; } PAR_real_token(); node = syntax_node(type, 2); if (type == nod_delete_proc) node->syn_arg[0] = (qli_syntax*) parse_qualified_procedure(); else { node->syn_arg[0] = (qli_syntax*) parse_database(); node->syn_arg[1] = (qli_syntax*) 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. * **************************************/ USHORT dtype; const KWWORDS keyword = QLI_token->tok_keyword; PAR_token(); *scale = 0; switch (keyword) { case KW_SHORT: *length = sizeof(SSHORT); dtype = dtype_short; break; case KW_BIGINT: *length = sizeof(SINT64); dtype = dtype_int64; 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 || dtype == dtype_int64 ) { if (PAR_match(KW_SCALE)) { const bool m = (PAR_match(KW_MINUS)) ? true : false; *scale = parse_ordinal(); if (m) *scale = -(*scale); } } else if (dtype == dtype_text || dtype == dtype_varying) { if (!PAR_match(KW_L_BRCKET) && !PAR_match(KW_LT)) ERRQ_syntax(174); /* Msg174 "[" */ USHORT l = parse_ordinal(); if (dtype == dtype_varying) l += sizeof(SSHORT); *length = l; if (!PAR_match(KW_R_BRCKET) && !PAR_match(KW_GT)) ERRQ_syntax(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, * **************************************/ // grab KW_SUB_TYPE PAR_token(); PAR_match(KW_IS); if (PAR_match(KW_TEXT) || PAR_match(KW_FIXED)) return 1; const int sign = (PAR_match(KW_MINUS)) ? -1 : 1; return (sign * parse_ordinal()); } static qli_syntax* 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 * * **************************************/ 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))) { qli_lls* statement_list = LEX_statement_list(); if (!statement_list) IBERROR(176); // Msg176 No statements issued yet if (PAR_match(KW_ASTERISK)) LEX_edit(0, (IPTR) statement_list->lls_object); else { int l = 0; // initialize, will catch changes in logic here. if (KEYWORD(KW_SEMI)) l = 1; else if (QLI_token->tok_type == tok_number) // redundant for now l = parse_ordinal(); qli_lls* start = statement_list; qli_lls* stop = start; while (l && start->lls_next) { --l; start = start->lls_next; } command_end(); LEX_edit((IPTR) start->lls_object, (IPTR) stop->lls_object); } } else { const NOD_T type = nod_edit_proc; qli_syntax* node = syntax_node(type, 2); node->syn_arg[0] = (qli_syntax*) 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 qli_syntax* 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 ] * **************************************/ PAR_token(); PAR_match(KW_ALL); PAR_match(KW_OF); qli_syntax* node = syntax_node(nod_erase, s_era_count); if (PAR_match(KW_ALL) || potential_rse()) { PAR_match(KW_OF); node->syn_arg[s_era_rse] = parse_rse(); } return node; } static qli_syntax* 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 ] * **************************************/ PAR_real_token(); qli_syntax* node = syntax_node(nod_extract, 2); node->syn_arg[1] = parse_output(); if (!PAR_match(KW_ALL)) { qli_lls* stack = NULL; for (;;) { ALLQ_push((blk*) parse_qualified_procedure(), &stack); if (!PAR_match(KW_COMMA)) break; } node->syn_arg[0] = make_list(stack); } if (!node->syn_arg[1] && !(node->syn_arg[1] = parse_output())) ERRQ_syntax(177); // Msg177 "ON or TO" return node; } static qli_fld* parse_field( bool global_flag) { /************************************** * * p a r s e _ f i e l d * ************************************** * * Functional description * Parse a field description. * **************************************/ PAR_real(); USHORT dtype = 0, length = 0, scale = 0; SSHORT sub_type = 0; SSHORT sub_type_missing = 1; qli_symbol* query_name = NULL; qli_symbol* based_on = NULL; const TEXT* edit_string = NULL; const TEXT* query_header = NULL; qli_symbol* 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) ERRQ_syntax(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) ERRQ_syntax(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(); PAR_match(KW_IS); if (QLI_token->tok_type != tok_ident) ERRQ_syntax(199); // Msg199 identifier query_name = parse_symbol(); break; case KW_BASED: PAR_token(); PAR_match(KW_ON); based_on = parse_symbol(); break; default: ERRQ_syntax(179); // Msg179 field definition clause break; } } qli_fld* 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 qli_syntax* parse_field_name( qli_syntax** 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. * **************************************/ qli_lls* stack = NULL; while (true) { if (PAR_match(KW_ASTERISK)) { if (!stack) ERRQ_syntax(181); // Msg181 field name or asterisk expression qli_syntax* afield = make_list(stack); afield->syn_type = nod_star; return afield; } ALLQ_push((blk*) parse_name(), &stack); if (!PAR_match(KW_DOT)) break; } qli_syntax* field = make_list(stack); field->syn_type = nod_field; if (fld_ptr) *fld_ptr = field; if (!(PAR_match(KW_L_BRCKET))) return field; // Parse an array reference stack = NULL; for (;;) { ALLQ_push((blk*) parse_value(0, 0), &stack); if (PAR_match(KW_R_BRCKET)) break; if (!PAR_match(KW_COMMA)) ERRQ_syntax(183); // Msg183 comma } qli_syntax* 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 qli_syntax* parse_for(void) { /************************************** * * p a r s e _ f o r * ************************************** * * Functional description * Parse a FOR statement. * **************************************/ PAR_token(); qli_syntax* 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; } static qli_syntax* parse_from( USHORT * paren_count, bool* bool_flag) { /************************************** * * p a r s e _ f r o m * ************************************** * * Functional description * Parse either an explicit or implicit FIRST ... FROM statement. * **************************************/ qli_syntax* value; PAR_real(); if (PAR_match(KW_FIRST)) { value = parse_primitive_value(0, 0); PAR_real(); if (!PAR_match(KW_FROM)) ERRQ_syntax(182); // Msg182 FROM rse clause } else { value = parse_primitive_value(paren_count, bool_flag); if (sql_flag || !PAR_match(KW_FROM)) return value; } qli_syntax* node = syntax_node(nod_from, s_stt_count); node->syn_arg[s_stt_value] = value; node->syn_arg[s_stt_rse] = parse_rse(); if (PAR_match(KW_ELSE)) node->syn_arg[s_stt_default] = parse_value(0, 0); return node; } static qli_syntax* parse_function(void) { /************************************** * * p a r s e _ f u n c t i o n * ************************************** * * Functional description * Parse a function reference. * **************************************/ function_count++; qli_syntax* node = syntax_node(nod_function, s_fun_count); node->syn_arg[s_fun_function] = (qli_syntax*) QLI_token->tok_symbol; node->syn_count = 1; PAR_token(); qli_lls* stack = NULL; if (PAR_match(KW_LEFT_PAREN)) for (;;) { ALLQ_push((blk*) parse_value(0, 0), &stack); if (PAR_match(KW_RIGHT_PAREN)) break; if (!PAR_match(KW_COMMA)) ERRQ_syntax(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 header[1024]; TEXT* p = header; const TEXT* end = p + sizeof(header); while (true) { PAR_real(); if ((QLI_token->tok_keyword != KW_MINUS) && (QLI_token->tok_type != tok_quoted)) { ERRQ_syntax(184); // Msg184 quoted header segment } const TEXT* q = QLI_token->tok_string; while (*q && p < end) *p++ = *q++; if (p == end && *q) ERRQ_syntax(184); // Msg184 quoted header segment PAR_real_token(); if (!PAR_match(KW_SLASH)) break; } return make_string(header, p - header); } static qli_syntax* 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. * **************************************/ qli_lls* stack = NULL; PAR_token(); while (!KEYWORD(KW_SEMI)) { ALLQ_push((blk*) parse_name(), &stack); PAR_match(KW_COMMA); } qli_syntax* node = make_list(stack); node->syn_type = nod_help; command_end(); return node; } static qli_syntax* parse_if(void) { /************************************** * * p a r s e _ i f * ************************************** * * Functional description * Parse an IF THEN ELSE statement. * **************************************/ PAR_token(); qli_syntax* node = syntax_node(nod_if, s_if_count); node->syn_arg[s_if_boolean] = parse_boolean(0); PAR_real(); PAR_match(KW_THEN); ++else_count; node->syn_arg[s_if_true] = parse_statement(); --else_count; if (PAR_match(KW_ELSE)) node->syn_arg[s_if_false] = parse_statement(); return node; } static qli_syntax* parse_in( qli_syntax* value, NOD_T operatr, bool 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