Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Postgres/src/interfaces/ecpg/preproc/   (Postgres Database Version 18.4©)  Datei vom 11.4.2026 mit Größe 48 kB image not shown  

Quelle  ecpg.trailer   Sprache: unbekannt

 
Spracherkennung für: .trailer vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

/* src/interfaces/ecpg/preproc/ecpg.trailer */

statements: /* EMPTY */
 | statements statement
 {
  /* Reclaim local storage used while processing statement */
  reclaim_local_storage();
  /* Clean up now-dangling location pointer */
  @$ = "";
 }
 ;

statement: ecpgstart at toplevel_stmt ';'
 {
  if (connection)
   free(connection);
  connection = NULL;
 }
 | ecpgstart toplevel_stmt ';'
 {
  if (connection)
   free(connection);
  connection = NULL;
 }
 | ecpgstart ECPGVarDeclaration
 {
  fprintf(base_yyout, "%s", @$);
  output_line_number();
 }
 | ECPGDeclaration
 | c_thing
 {
  fprintf(base_yyout, "%s", @$);
 }
 | CPP_LINE
 {
  fprintf(base_yyout, "%s", @$);
 }
 | '{'
 {
  braces_open++;
  fputs("{", base_yyout);
 }
 | '}'
 {
  if (braces_open > 0)
  {
   remove_typedefs(braces_open);
   remove_variables(braces_open);
   if (--braces_open == 0)
   {
    free(current_function);
    current_function = NULL;
   }
  }
  fputs("}", base_yyout);
 }
 ;

CreateAsStmt: CREATE OptTemp TABLE create_as_target AS
 {
  FoundInto = 0;
 } SelectStmt opt_with_data
 {
  if (FoundInto == 1)
   mmerror(PARSE_ERROR, ET_ERROR, "CREATE TABLE AS cannot specify INTO");
 }
 | CREATE OptTemp TABLE IF_P NOT EXISTS create_as_target AS
 {
  FoundInto = 0;
 } SelectStmt opt_with_data
 {
  if (FoundInto == 1)
   mmerror(PARSE_ERROR, ET_ERROR, "CREATE TABLE AS cannot specify INTO");
 }
 ;

at: AT connection_object
 {
  if (connection)
   free(connection);
  connection = mm_strdup(@2);

  /*
   * Do we have a variable as connection target?  Remove the variable
   * from the variable list or else it will be used twice.
   */
  if (argsinsert != NULL)
   argsinsert = NULL;
 }
 ;

/*
 * the exec sql connect statement: connect to the given database
 */
ECPGConnect: SQL_CONNECT TO connection_target opt_connection_name opt_user
 {
  @$ = cat_str(5, @3, ",", @5, ",", @4);
 }
 | SQL_CONNECT TO DEFAULT
 {
  @$ = "NULL, NULL, NULL, \"DEFAULT\"";
 }
 /* also allow ORACLE syntax */
 | SQL_CONNECT ora_user
 {
  @$ = cat_str(3, "NULL,", @2, ", NULL");
 }
 | DATABASE connection_target
 {
  @$ = cat2_str(@2, ", NULL, NULL, NULL");
 }
 ;

connection_target: opt_database_name opt_server opt_port
 {
  /* old style: dbname[@server][:port] */
  if (strlen(@2) > 0 && *(@2) != '@')
   mmerror(PARSE_ERROR, ET_ERROR, "expected \"@\", found \"%s\"", @2);

  /* C strings need to be handled differently */
  if (@1[0] == '\"')
   @$ = @1;
  else
   @$ = make3_str("\"", make3_str(@1, @2, @3), "\"");
 }
 | db_prefix ':' server opt_port '/' opt_database_name opt_options
 {
  /* new style: <tcp|unix>:postgresql://server[:port][/dbname] */
  if (strncmp(@1, "unix:postgresql", strlen("unix:postgresql")) != 0 && strncmp(@1, "tcp:postgresql", strlen("tcp:postgresql")) != 0)
   mmerror(PARSE_ERROR, ET_ERROR, "only protocols \"tcp\" and \"unix\" and database type \"postgresql\" are supported");

  if (strncmp(@3, "//", strlen("//")) != 0)
   mmerror(PARSE_ERROR, ET_ERROR, "expected \"://\", found \"%s\"", @3);

  if (strncmp(@1, "unix", strlen("unix")) == 0 &&
   strncmp(@3 + strlen("//"), "localhost", strlen("localhost")) != 0 &&
   strncmp(@3 + strlen("//"), "127.0.0.1", strlen("127.0.0.1")) != 0)
   mmerror(PARSE_ERROR, ET_ERROR, "Unix-domain sockets only work on \"localhost\" but not on \"%s\"", @3 + strlen("//"));

  @$ = make3_str(make3_str("\"", @1, ":"), @3, make3_str(make3_str(@4, "/", @6), @7, "\""));
 }
 | char_variable
 | ecpg_sconst
 {
  /*
   * We can only process double quoted strings not single quoted ones,
   * so we change the quotes. Note that the rule for ecpg_sconst adds
   * these single quotes.
   */
  char   *str = loc_strdup(@1);

  str[0] = '\"';
  str[strlen(str) - 1] = '\"';
  @$ = str;
 }
 ;

opt_database_name: name
 | /* EMPTY */
 ;

db_prefix: ecpg_ident cvariable
 {
  if (strcmp(@2, "postgresql") != 0 && strcmp(@2, "postgres") != 0)
   mmerror(PARSE_ERROR, ET_ERROR, "expected \"postgresql\", found \"%s\"", @2);

  if (strcmp(@1, "tcp") != 0 && strcmp(@1, "unix") != 0)
   mmerror(PARSE_ERROR, ET_ERROR, "invalid connection type: %s", @1);

  @$ = make3_str(@1, ":", @2);
 }
 ;

server: Op server_name
 {
  if (strcmp(@1, "@") != 0 && strcmp(@1, "//") != 0)
   mmerror(PARSE_ERROR, ET_ERROR, "expected \"@\" or \"://\", found \"%s\"", @1);

  @$ = make2_str(@1, @2);
 }
 ;

opt_server: server
 | /* EMPTY */
 ;

server_name: ColId
 | ColId '.' server_name
 | IP
 ;

opt_port: ':' Iconst
 {
  @$ = make2_str(":", @2);
 }
 | /* EMPTY */
 ;

opt_connection_name: AS connection_object
 {
  @$ = @2;
 }
 | /* EMPTY */
 {
  @$ = "NULL";
 }
 ;

opt_user: USER ora_user
 {
  @$ = @2;
 }
 | /* EMPTY */
 {
  @$ = "NULL, NULL";
 }
 ;

ora_user: user_name
 {
  @$ = cat2_str(@1, ", NULL");
 }
 | user_name '/' user_name
 {
  @$ = cat_str(3, @1, ",", @3);
 }
 | user_name SQL_IDENTIFIED BY user_name
 {
  @$ = cat_str(3, @1, ",", @4);
 }
 | user_name USING user_name
 {
  @$ = cat_str(3, @1, ",", @3);
 }
 ;

user_name: RoleId
 {
  if (@1[0] == '\"')
   @$ = @1;
  else
   @$ = make3_str("\"", @1, "\"");
 }
 | ecpg_sconst
 {
  if (@1[0] == '\"')
   @$ = @1;
  else
   @$ = make3_str("\"", @1, "\"");
 }
 | civar
 {
  enum ECPGttype type = argsinsert->variable->type->type;

  /* if array see what's inside */
  if (type == ECPGt_array)
   type = argsinsert->variable->type->u.element->type;

  /* handle varchars */
  if (type == ECPGt_varchar)
   @$ = make2_str(argsinsert->variable->name, ".arr");
  else
   @$ = argsinsert->variable->name;
 }
 ;

char_variable: cvariable
 {
  /* check if we have a string variable */
  struct variable *p = find_variable(@1);
  enum ECPGttype type = p->type->type;

  /* If we have just one character this is not a string */
  if (atol(p->type->size) == 1)
   mmerror(PARSE_ERROR, ET_ERROR, "invalid data type");
  else
  {
   /* if array see what's inside */
   if (type == ECPGt_array)
    type = p->type->u.element->type;

   switch (type)
   {
    case ECPGt_char:
    case ECPGt_unsigned_char:
    case ECPGt_string:
     @$ = @1;
     break;
    case ECPGt_varchar:
     @$ = make2_str(@1, ".arr");
     break;
    default:
     mmerror(PARSE_ERROR, ET_ERROR, "invalid data type");
     @$ = @1;
     break;
   }
  }
 }
 ;

opt_options: Op connect_options
 {
  if (strlen(@1) == 0)
   mmerror(PARSE_ERROR, ET_ERROR, "incomplete statement");

  if (strcmp(@1, "?") != 0)
   mmerror(PARSE_ERROR, ET_ERROR, "unrecognized token \"%s\"", @1);

  @$ = make2_str("?", @2);
 }
 | /* EMPTY */
 ;

connect_options: ColId opt_opt_value
 {
  @$ = make2_str(@1, @2);
 }
 | ColId opt_opt_value Op connect_options
 {
  if (strlen(@3) == 0)
   mmerror(PARSE_ERROR, ET_ERROR, "incomplete statement");

  if (strcmp(@3, "&") != 0)
   mmerror(PARSE_ERROR, ET_ERROR, "unrecognized token \"%s\"", @3);

  @$ = make3_str(make2_str(@1, @2), @3, @4);
 }
 ;

opt_opt_value: /* EMPTY */
 | '=' Iconst
 {
  @$ = make2_str("=", @2);
 }
 | '=' ecpg_ident
 {
  @$ = make2_str("=", @2);
 }
 | '=' civar
 {
  @$ = make2_str("=", @2);
 }
 ;

prepared_name: name
 {
  size_t  slen = strlen(@1);

  if (@1[0] == '\"' && @1[slen - 1] == '\"') /* already quoted? */
   @$ = @1;
  else     /* not quoted => convert to lowercase */
  {
   char    *str = loc_alloc(slen + 3);

   str[0] = '\"';
   for (size_t i = 0; i < slen; i++)
    str[i + 1] = tolower((unsigned char) @1[i]);
   str[slen + 1] = '\"';
   str[slen + 2] = '\0';
   @$ = str;
  }
 }
 | char_variable
 ;

/*
 * Declare Statement
 */
ECPGDeclareStmt: DECLARE prepared_name STATEMENT
 {
  struct declared_list *ptr;

  /* Check whether the declared name has been defined or not */
  for (ptr = g_declared_list; ptr != NULL; ptr = ptr->next)
  {
   if (strcmp(@2, ptr->name) == 0)
   {
    /* re-definition is not allowed */
    mmerror(PARSE_ERROR, ET_ERROR, "name \"%s\" is already declared", ptr->name);
   }
  }

  /* Add a new declared name into the g_declared_list */
  ptr = (struct declared_list *) mm_alloc(sizeof(struct declared_list));
  if (ptr)
  {
   /* initial definition */
   ptr->name = mm_strdup(@2);
   if (connection)
    ptr->connection = mm_strdup(connection);
   else
    ptr->connection = NULL;

   ptr->next = g_declared_list;
   g_declared_list = ptr;
  }

  @$ = cat_str(3, "/* declare ", @2, " as an SQL identifier */");
 }
 ;

/*
 * Declare a prepared cursor. The syntax is different from the standard
 * declare statement, so we create a new rule.
 */
ECPGCursorStmt: DECLARE cursor_name cursor_options CURSOR opt_hold FOR prepared_name
 {
  struct cursor *ptr,
       *this;
  const char *cursor_marker = @2[0] == ':' ? "$0" : @2;
  int   (*strcmp_fn) (const char *, const char *) = ((@2[0] == ':' || @2[0] == '"') ? strcmp : pg_strcasecmp);
  struct variable *thisquery = (struct variable *) mm_alloc(sizeof(struct variable));
  char    *comment;
  char    *con;

  if (INFORMIX_MODE && pg_strcasecmp(@2, "database") == 0)
   mmfatal(PARSE_ERROR, "\"database\" cannot be used as cursor name in INFORMIX mode");

  check_declared_list(@7);
  con = connection ? connection : "NULL";
  for (ptr = cur; ptr != NULL; ptr = ptr->next)
  {
   if (strcmp_fn(@2, ptr->name) == 0)
   {
    /* re-definition is a bug */
    if (@2[0] == ':')
     mmerror(PARSE_ERROR, ET_ERROR, "using variable \"%s\" in different declare statements is not supported", @2 + 1);
    else
     mmerror(PARSE_ERROR, ET_ERROR, "cursor \"%s\" is already defined", @2);
   }
  }

  this = (struct cursor *) mm_alloc(sizeof(struct cursor));

  /* initial definition */
  this->next = cur;
  this->name = mm_strdup(@2);
  this->function = (current_function ? mm_strdup(current_function) : NULL);
  this->connection = connection ? mm_strdup(connection) : NULL;
  this->opened = false;
  this->command = mm_strdup(cat_str(6, "declare", cursor_marker, @3, "cursor", @5, "for $1"));
  this->argsresult = NULL;
  this->argsresult_oos = NULL;

  thisquery->type = &ecpg_query;
  thisquery->brace_level = 0;
  thisquery->next = NULL;
  thisquery->name = (char *) mm_alloc(sizeof("ECPGprepared_statement(, , __LINE__)") + strlen(con) + strlen(@7));
  sprintf(thisquery->name, "ECPGprepared_statement(%s, %s, __LINE__)", con, @7);

  this->argsinsert = NULL;
  this->argsinsert_oos = NULL;
  if (@2[0] == ':')
  {
   struct variable *var = find_variable(@2 + 1);

   remove_variable_from_list(&argsinsert, var);
   add_variable_to_head(&(this->argsinsert), var, &no_indicator);
  }
  add_variable_to_head(&(this->argsinsert), thisquery, &no_indicator);

  cur = this;

  comment = cat_str(3, "/*", this->command, "*/");

  @$ = cat_str(2, adjust_outofscope_cursor_vars(this),
      comment);
 }
 ;

ECPGExecuteImmediateStmt: EXECUTE IMMEDIATE execstring
 {
  /*
   * execute immediate means prepare the statement and immediately
   * execute it
   */
  @$ = @3;
 }
 ;

/*
 * variable declaration outside exec sql declare block
 */
ECPGVarDeclaration: single_vt_declaration;

single_vt_declaration: type_declaration
 | var_declaration
 ;

precision: NumericOnly
 ;

opt_scale: ',' NumericOnly
 {
  @$ = @2;
 }
 | /* EMPTY */
 ;

ecpg_interval: opt_interval
 | YEAR_P TO MINUTE_P
 | YEAR_P TO SECOND_P
 | DAY_P TO DAY_P
 | MONTH_P TO MONTH_P
 ;

/*
 * variable declaration inside exec sql declare block
 */
ECPGDeclaration: sql_startdeclare
 {
  fputs("/* exec sql begin declare section */", base_yyout);
 }
 var_type_declarations sql_enddeclare
 {
  fprintf(base_yyout, "%s/* exec sql end declare section */", @3);
  output_line_number();
 }
 ;

sql_startdeclare: ecpgstart BEGIN_P DECLARE SQL_SECTION ';'
 {
 }
 ;

sql_enddeclare: ecpgstart END_P DECLARE SQL_SECTION ';'
 {
 }
 ;

var_type_declarations: /* EMPTY */
 | vt_declarations
 ;

vt_declarations: single_vt_declaration
 | CPP_LINE
 | vt_declarations single_vt_declaration
 | vt_declarations CPP_LINE
 ;

variable_declarations: var_declaration
 | variable_declarations var_declaration
 ;

type_declaration: S_TYPEDEF
 {
  /* reset this variable so we see if there was */
  /* an initializer specified */
  initializer = 0;
 }
 var_type opt_pointer ECPGColLabel opt_array_bounds ';'
 {
  add_typedef(@5, $6.index1, $6.index2, $3.type_enum, $3.type_dimension, $3.type_index, initializer, *@4 ? 1 : 0);

  fprintf(base_yyout, "typedef %s %s %s %s;\n", $3.type_str, *@4 ? "*" : "", @5, $6.str);
  output_line_number();
  @$ = "";
 }
 ;

var_declaration:
 storage_declaration var_type
 {
  actual_type[struct_level].type_storage = loc_strdup(@1);
  actual_type[struct_level].type_enum = $2.type_enum;
  actual_type[struct_level].type_str = $2.type_str;
  actual_type[struct_level].type_dimension = $2.type_dimension;
  actual_type[struct_level].type_index = $2.type_index;
  actual_type[struct_level].type_sizeof = $2.type_sizeof;

  actual_startline[struct_level] = hashline_number();
 }
 variable_list ';'
 {
  @$ = cat_str(5, actual_startline[struct_level], @1, $2.type_str, @4, ";\n");
 }
 | var_type
 {
  actual_type[struct_level].type_storage = loc_strdup("");
  actual_type[struct_level].type_enum = $1.type_enum;
  actual_type[struct_level].type_str = $1.type_str;
  actual_type[struct_level].type_dimension = $1.type_dimension;
  actual_type[struct_level].type_index = $1.type_index;
  actual_type[struct_level].type_sizeof = $1.type_sizeof;

  actual_startline[struct_level] = hashline_number();
 }
 variable_list ';'
 {
  @$ = cat_str(4, actual_startline[struct_level], $1.type_str, @3, ";\n");
 }
 | struct_union_type_with_symbol ';'
 ;

opt_bit_field: ':' Iconst
 | /* EMPTY */
 ;

storage_declaration: storage_clause storage_modifier
 | storage_clause
 | storage_modifier
 ;

storage_clause: S_EXTERN
 | S_STATIC
 | S_REGISTER
 | S_AUTO
 ;

storage_modifier: S_CONST
 | S_VOLATILE
 ;

var_type: simple_type
 {
  $$.type_enum = $1;
  $$.type_str = loc_strdup(ecpg_type_name($1));
  $$.type_dimension = "-1";
  $$.type_index = "-1";
  $$.type_sizeof = NULL;
 }
 | struct_union_type
 {
  $$.type_str = loc_strdup(@1);
  $$.type_dimension = "-1";
  $$.type_index = "-1";

  if (strncmp(@1, "struct", sizeof("struct") - 1) == 0)
  {
   $$.type_enum = ECPGt_struct;
   $$.type_sizeof = ECPGstruct_sizeof;
  }
  else
  {
   $$.type_enum = ECPGt_union;
   $$.type_sizeof = NULL;
  }
 }
 | enum_type
 {
  $$.type_str = loc_strdup(@1);
  $$.type_enum = ECPGt_int;
  $$.type_dimension = "-1";
  $$.type_index = "-1";
  $$.type_sizeof = NULL;
 }
 | NUMERIC '(' precision opt_scale ')'
 {
  $$.type_enum = ECPGt_numeric;
  $$.type_str = "numeric";
  $$.type_dimension = "-1";
  $$.type_index = "-1";
  $$.type_sizeof = NULL;
 }
 | DECIMAL_P '(' precision opt_scale ')'
 {
  $$.type_enum = ECPGt_decimal;
  $$.type_str = "decimal";
  $$.type_dimension = "-1";
  $$.type_index = "-1";
  $$.type_sizeof = NULL;
 }
 | IDENT '(' precision opt_scale ')'
 {
  /*
   * In C parsing mode, NUMERIC and DECIMAL are not keywords, so they
   * will show up here as a plain identifier, and we need this duplicate
   * code to recognize them.
   */
  if (strcmp(@1, "numeric") == 0)
  {
   $$.type_enum = ECPGt_numeric;
   $$.type_str = "numeric";
  }
  else if (strcmp(@1, "decimal") == 0)
  {
   $$.type_enum = ECPGt_decimal;
   $$.type_str = "decimal";
  }
  else
  {
   mmerror(PARSE_ERROR, ET_ERROR, "only data types numeric and decimal have precision/scale argument");
   $$.type_enum = ECPGt_numeric;
   $$.type_str = "numeric";
  }

  $$.type_dimension = "-1";
  $$.type_index = "-1";
  $$.type_sizeof = NULL;
 }
 | VARCHAR
 {
  $$.type_enum = ECPGt_varchar;
  $$.type_str = ""; /* "varchar"; */
  $$.type_dimension = "-1";
  $$.type_index = "-1";
  $$.type_sizeof = NULL;
 }
 | FLOAT_P
 {
  /* Note: DOUBLE is handled in simple_type */
  $$.type_enum = ECPGt_float;
  $$.type_str = "float";
  $$.type_dimension = "-1";
  $$.type_index = "-1";
  $$.type_sizeof = NULL;
 }
 | NUMERIC
 {
  $$.type_enum = ECPGt_numeric;
  $$.type_str = "numeric";
  $$.type_dimension = "-1";
  $$.type_index = "-1";
  $$.type_sizeof = NULL;
 }
 | DECIMAL_P
 {
  $$.type_enum = ECPGt_decimal;
  $$.type_str = "decimal";
  $$.type_dimension = "-1";
  $$.type_index = "-1";
  $$.type_sizeof = NULL;
 }
 | TIMESTAMP
 {
  $$.type_enum = ECPGt_timestamp;
  $$.type_str = "timestamp";
  $$.type_dimension = "-1";
  $$.type_index = "-1";
  $$.type_sizeof = NULL;
 }
 | STRING_P
 {
  if (INFORMIX_MODE)
  {
   /* In Informix mode, "string" is automatically a typedef */
   $$.type_enum = ECPGt_string;
   $$.type_str = "char";
   $$.type_dimension = "-1";
   $$.type_index = "-1";
   $$.type_sizeof = NULL;
  }
  else
  {
   /* Otherwise, legal only if user typedef'ed it */
   struct typedefs *this = get_typedef("string", false);

   $$.type_str = (this->type->type_enum == ECPGt_varchar || this->type->type_enum == ECPGt_bytea) ? mm_strdup("") : mm_strdup(this->name);
   $$.type_enum = this->type->type_enum;
   $$.type_dimension = this->type->type_dimension;
   $$.type_index = this->type->type_index;
   if (this->type->type_sizeof && strlen(this->type->type_sizeof) != 0)
    $$.type_sizeof = this->type->type_sizeof;
   else
    $$.type_sizeof = cat_str(3, "sizeof(", this->name, ")");

   ECPGfree_struct_member(struct_member_list[struct_level]);
   struct_member_list[struct_level] = ECPGstruct_member_dup(this->struct_member_list);
  }
 }
 | INTERVAL ecpg_interval
 {
  $$.type_enum = ECPGt_interval;
  $$.type_str = "interval";
  $$.type_dimension = "-1";
  $$.type_index = "-1";
  $$.type_sizeof = NULL;
 }
 | IDENT ecpg_interval
 {
  /*
   * In C parsing mode, the above SQL type names are not keywords, so
   * they will show up here as a plain identifier, and we need this
   * duplicate code to recognize them.
   *
   * Note that we also handle the type names bytea, date, and datetime
   * here, but not above because those are not currently SQL keywords.
   * If they ever become so, they must gain duplicate productions above.
   */
  if (strlen(@2) != 0 && strcmp(@1, "datetime") != 0 && strcmp(@1, "interval") != 0)
   mmerror(PARSE_ERROR, ET_ERROR, "interval specification not allowed here");

  if (strcmp(@1, "varchar") == 0)
  {
   $$.type_enum = ECPGt_varchar;
   $$.type_str = ""; /* "varchar"; */
   $$.type_dimension = "-1";
   $$.type_index = "-1";
   $$.type_sizeof = NULL;
  }
  else if (strcmp(@1, "bytea") == 0)
  {
   $$.type_enum = ECPGt_bytea;
   $$.type_str = "";
   $$.type_dimension = "-1";
   $$.type_index = "-1";
   $$.type_sizeof = NULL;
  }
  else if (strcmp(@1, "float") == 0)
  {
   $$.type_enum = ECPGt_float;
   $$.type_str = "float";
   $$.type_dimension = "-1";
   $$.type_index = "-1";
   $$.type_sizeof = NULL;
  }
  else if (strcmp(@1, "double") == 0)
  {
   $$.type_enum = ECPGt_double;
   $$.type_str = "double";
   $$.type_dimension = "-1";
   $$.type_index = "-1";
   $$.type_sizeof = NULL;
  }
  else if (strcmp(@1, "numeric") == 0)
  {
   $$.type_enum = ECPGt_numeric;
   $$.type_str = "numeric";
   $$.type_dimension = "-1";
   $$.type_index = "-1";
   $$.type_sizeof = NULL;
  }
  else if (strcmp(@1, "decimal") == 0)
  {
   $$.type_enum = ECPGt_decimal;
   $$.type_str = "decimal";
   $$.type_dimension = "-1";
   $$.type_index = "-1";
   $$.type_sizeof = NULL;
  }
  else if (strcmp(@1, "date") == 0)
  {
   $$.type_enum = ECPGt_date;
   $$.type_str = "date";
   $$.type_dimension = "-1";
   $$.type_index = "-1";
   $$.type_sizeof = NULL;
  }
  else if (strcmp(@1, "timestamp") == 0)
  {
   $$.type_enum = ECPGt_timestamp;
   $$.type_str = "timestamp";
   $$.type_dimension = "-1";
   $$.type_index = "-1";
   $$.type_sizeof = NULL;
  }
  else if (strcmp(@1, "interval") == 0)
  {
   $$.type_enum = ECPGt_interval;
   $$.type_str = "interval";
   $$.type_dimension = "-1";
   $$.type_index = "-1";
   $$.type_sizeof = NULL;
  }
  else if (strcmp(@1, "datetime") == 0)
  {
   $$.type_enum = ECPGt_timestamp;
   $$.type_str = "timestamp";
   $$.type_dimension = "-1";
   $$.type_index = "-1";
   $$.type_sizeof = NULL;
  }
  else if ((strcmp(@1, "string") == 0) && INFORMIX_MODE)
  {
   $$.type_enum = ECPGt_string;
   $$.type_str = "char";
   $$.type_dimension = "-1";
   $$.type_index = "-1";
   $$.type_sizeof = NULL;
  }
  else
  {
   /* Otherwise, it must be a user-defined typedef name */
   struct typedefs *this = get_typedef(@1, false);

   $$.type_str = (this->type->type_enum == ECPGt_varchar || this->type->type_enum == ECPGt_bytea) ? "" : this->name;
   $$.type_enum = this->type->type_enum;
   $$.type_dimension = this->type->type_dimension;
   $$.type_index = this->type->type_index;
   if (this->type->type_sizeof && strlen(this->type->type_sizeof) != 0)
    $$.type_sizeof = this->type->type_sizeof;
   else
    $$.type_sizeof = cat_str(3, "sizeof(", this->name, ")");

   ECPGfree_struct_member(struct_member_list[struct_level]);
   struct_member_list[struct_level] = ECPGstruct_member_dup(this->struct_member_list);
  }
 }
 | s_struct_union_symbol
 {
  /* this is for named structs/unions */
  char    *name;
  struct typedefs *this;
  bool  forward = (forward_name != NULL && strcmp($1.symbol, forward_name) == 0 && strcmp($1.su, "struct") == 0);

  name = cat2_str($1.su, $1.symbol);
  /* Do we have a forward definition? */
  if (!forward)
  {
   /* No */

   this = get_typedef(name, false);
   $$.type_str = this->name;
   $$.type_enum = this->type->type_enum;
   $$.type_dimension = this->type->type_dimension;
   $$.type_index = this->type->type_index;
   $$.type_sizeof = this->type->type_sizeof;
   ECPGfree_struct_member(struct_member_list[struct_level]);
   struct_member_list[struct_level] = ECPGstruct_member_dup(this->struct_member_list);
  }
  else
  {
   $$.type_str = name;
   $$.type_enum = ECPGt_long;
   $$.type_dimension = "-1";
   $$.type_index = "-1";
   $$.type_sizeof = "";
   ECPGfree_struct_member(struct_member_list[struct_level]);
   struct_member_list[struct_level] = NULL;
  }
 }
 ;

enum_type: ENUM_P symbol enum_definition
 | ENUM_P enum_definition
 | ENUM_P symbol
 ;

enum_definition: '{' c_list '}'
 ;

struct_union_type_with_symbol: s_struct_union_symbol
 {
  ECPGfree_struct_member(struct_member_list[struct_level]);
  struct_member_list[struct_level++] = NULL;
  if (struct_level >= STRUCT_DEPTH)
   mmerror(PARSE_ERROR, ET_ERROR, "too many levels in nested structure/union definition");
  forward_name = mm_strdup($1.symbol);
 }
 '{' variable_declarations '}'
 {
  struct typedefs *ptr,
       *this;
  struct this_type su_type;

  ECPGfree_struct_member(struct_member_list[struct_level]);
  struct_member_list[struct_level] = NULL;
  struct_level--;
  if (strcmp($1.su, "struct") == 0)
   su_type.type_enum = ECPGt_struct;
  else
   su_type.type_enum = ECPGt_union;
  su_type.type_str = cat2_str($1.su, $1.symbol);
  free(forward_name);
  forward_name = NULL;

  /*
   * This is essentially a typedef but needs the keyword struct/union as
   * well. So we create the typedef for each struct definition with
   * symbol
   */
  for (ptr = types; ptr != NULL; ptr = ptr->next)
  {
   if (strcmp(su_type.type_str, ptr->name) == 0)
    /* re-definition is a bug */
    mmerror(PARSE_ERROR, ET_ERROR, "type \"%s\" is already defined", su_type.type_str);
  }

  this = (struct typedefs *) mm_alloc(sizeof(struct typedefs));

  /* initial definition */
  this->next = types;
  this->name = mm_strdup(su_type.type_str);
  this->brace_level = braces_open;
  this->type = (struct this_type *) mm_alloc(sizeof(struct this_type));
  this->type->type_storage = NULL;
  this->type->type_enum = su_type.type_enum;
  this->type->type_str = mm_strdup(su_type.type_str);
  this->type->type_dimension = mm_strdup("-1"); /* dimension of array */
  this->type->type_index = mm_strdup("-1"); /* length of string */
  this->type->type_sizeof = ECPGstruct_sizeof ? mm_strdup(ECPGstruct_sizeof) : NULL;
  this->struct_member_list = ECPGstruct_member_dup(struct_member_list[struct_level]);

  types = this;
  @$ = cat_str(4, su_type.type_str, "{", @4, "}");
 }
 ;

struct_union_type: struct_union_type_with_symbol
 | s_struct_union
 {
  ECPGfree_struct_member(struct_member_list[struct_level]);
  struct_member_list[struct_level++] = NULL;
  if (struct_level >= STRUCT_DEPTH)
   mmerror(PARSE_ERROR, ET_ERROR, "too many levels in nested structure/union definition");
 }
 '{' variable_declarations '}'
 {
  ECPGfree_struct_member(struct_member_list[struct_level]);
  struct_member_list[struct_level] = NULL;
  struct_level--;
  @$ = cat_str(4, @1, "{", @4, "}");
 }
 ;

s_struct_union_symbol: SQL_STRUCT symbol
 {
  $$.su = "struct";
  $$.symbol = @2;
  free(ECPGstruct_sizeof);
  ECPGstruct_sizeof = mm_strdup(cat_str(3, "sizeof(",
             cat2_str($$.su, $$.symbol),
             ")"));
 }
 | UNION symbol
 {
  $$.su = "union";
  $$.symbol = @2;
 }
 ;

s_struct_union: SQL_STRUCT
 {
  free(ECPGstruct_sizeof);
  ECPGstruct_sizeof = mm_strdup(""); /* This must not be NULL to
            * distinguish from simple types. */
  @$ = "struct";
 }
 | UNION
 {
  @$ = "union";
 }
 ;

simple_type: unsigned_type
 | opt_signed signed_type   { $$ = $2; }
 ;

unsigned_type: SQL_UNSIGNED SQL_SHORT { $$ = ECPGt_unsigned_short; }
 | SQL_UNSIGNED SQL_SHORT INT_P  { $$ = ECPGt_unsigned_short; }
 | SQL_UNSIGNED      { $$ = ECPGt_unsigned_int; }
 | SQL_UNSIGNED INT_P    { $$ = ECPGt_unsigned_int; }
 | SQL_UNSIGNED SQL_LONG    { $$ = ECPGt_unsigned_long; }
 | SQL_UNSIGNED SQL_LONG INT_P  { $$ = ECPGt_unsigned_long; }
 | SQL_UNSIGNED SQL_LONG SQL_LONG { $$ = ECPGt_unsigned_long_long; }
 | SQL_UNSIGNED SQL_LONG SQL_LONG INT_P { $$ = ECPGt_unsigned_long_long; }
 | SQL_UNSIGNED CHAR_P    { $$ = ECPGt_unsigned_char; }
 ;

signed_type: SQL_SHORT     { $$ = ECPGt_short; }
 | SQL_SHORT INT_P     { $$ = ECPGt_short; }
 | INT_P        { $$ = ECPGt_int; }
 | SQL_LONG       { $$ = ECPGt_long; }
 | SQL_LONG INT_P     { $$ = ECPGt_long; }
 | SQL_LONG SQL_LONG     { $$ = ECPGt_long_long; }
 | SQL_LONG SQL_LONG INT_P   { $$ = ECPGt_long_long; }
 | SQL_BOOL       { $$ = ECPGt_bool; }
 | CHAR_P       { $$ = ECPGt_char; }
 | DOUBLE_P       { $$ = ECPGt_double; }
 ;

opt_signed: SQL_SIGNED
 | /* EMPTY */
 ;

variable_list: variable
 | variable_list ',' variable
 {
  if (actual_type[struct_level].type_enum == ECPGt_varchar || actual_type[struct_level].type_enum == ECPGt_bytea)
   @$ = cat_str(4, @1, ";", actual_type[struct_level].type_storage, @3);
  else
   @$ = cat_str(3, @1, ",", @3);
 }
 ;

variable: opt_pointer ECPGColLabel opt_array_bounds opt_bit_field opt_initializer
 {
  struct ECPGtype *type;
  const char *dimension = $3.index1; /* dimension of array */
  const char *length = $3.index2; /* length of string */
  char    *dim_str;
  char  vcn[32];
  int     *varlen_type_counter;
  char    *struct_name;

  adjust_array(actual_type[struct_level].type_enum,
      &dimension, &length,
      actual_type[struct_level].type_dimension,
      actual_type[struct_level].type_index,
      strlen(@1), false);
  switch (actual_type[struct_level].type_enum)
  {
   case ECPGt_struct:
   case ECPGt_union:
    if (atoi(dimension) < 0)
     type = ECPGmake_struct_type(struct_member_list[struct_level], actual_type[struct_level].type_enum, actual_type[struct_level].type_str, actual_type[struct_level].type_sizeof);
    else
     type = ECPGmake_array_type(ECPGmake_struct_type(struct_member_list[struct_level], actual_type[struct_level].type_enum, actual_type[struct_level].type_str, actual_type[struct_level].type_sizeof), dimension);

    @$ = cat_str(5, @1, @2, $3.str, @4, @5);
    break;

   case ECPGt_varchar:
   case ECPGt_bytea:
    if (actual_type[struct_level].type_enum == ECPGt_varchar)
    {
     varlen_type_counter = &varchar_counter;
     struct_name = " struct varchar_";
    }
    else
    {
     varlen_type_counter = &bytea_counter;
     struct_name = " struct bytea_";
    }
    if (atoi(dimension) < 0)
     type = ECPGmake_simple_type(actual_type[struct_level].type_enum, length, *varlen_type_counter);
    else
     type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, length, *varlen_type_counter), dimension);

    if (strcmp(dimension, "0") == 0 || abs(atoi(dimension)) == 1)
     dim_str = "";
    else
     dim_str = cat_str(3, "[", dimension, "]");

    /*
     * cannot check for atoi <= 0 because a defined constant will
     * yield 0 here as well
     */
    if (atoi(length) < 0 || strcmp(length, "0") == 0)
     mmerror(PARSE_ERROR, ET_ERROR, "pointers to varchar are not implemented");

    /*
     * make sure varchar struct name is unique by adding a unique
     * counter to its definition
     */
    snprintf(vcn, sizeof(vcn), "%d", *varlen_type_counter);
    if (strcmp(dimension, "0") == 0)
     @$ = cat_str(7, make2_str(struct_name, vcn), " { int len; char arr[", length, "]; } *", @2, @4, @5);
    else
     @$ = cat_str(8, make2_str(struct_name, vcn), " { int len; char arr[", length, "]; } ", @2, dim_str, @4, @5);
    (*varlen_type_counter)++;
    break;

   case ECPGt_char:
   case ECPGt_unsigned_char:
   case ECPGt_string:
    if (atoi(dimension) == -1)
    {
     int   i = strlen(@5);

     if (atoi(length) == -1 && i > 0) /* char <var>[] =
               * "string" */
     {
      /*
       * if we have an initializer but no string size set,
       * let's use the initializer's length
       */
      char   *buf = loc_alloc(32);

      snprintf(buf, 32, "sizeof(%s)", @5 + 2);
      length = buf;
     }
     type = ECPGmake_simple_type(actual_type[struct_level].type_enum, length, 0);
    }
    else
     type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, length, 0), dimension);

    @$ = cat_str(5, @1, @2, $3.str, @4, @5);
    break;

   default:
    if (atoi(dimension) < 0)
     type = ECPGmake_simple_type(actual_type[struct_level].type_enum, "1", 0);
    else
     type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, "1", 0), dimension);

    @$ = cat_str(5, @1, @2, $3.str, @4, @5);
    break;
  }

  if (struct_level == 0)
   new_variable(@2, type, braces_open);
  else
   ECPGmake_struct_member(@2, type, &(struct_member_list[struct_level - 1]));
 }
 ;

opt_initializer: /* EMPTY */
 | '=' c_term
 {
  initializer = 1;
 }
 ;

opt_pointer: /* EMPTY */
 | '*'
 | '*' '*'
 {
  @$ = "**";
 }
 ;

/*
 * We try to simulate the correct DECLARE syntax here so we get dynamic SQL
 */
ECPGDeclare: DECLARE STATEMENT ecpg_ident
 {
  /* this is only supported for compatibility */
  @$ = cat_str(3, "/* declare statement", @3, "*/");
 }
 ;
/*
 * the exec sql disconnect statement: disconnect from the given database
 */
ECPGDisconnect: SQL_DISCONNECT dis_name
 {
  @$ = @2;
 }
 ;

dis_name: connection_object
 | CURRENT_P
 {
  @$ = "\"CURRENT\"";
 }
 | ALL
 {
  @$ = "\"ALL\"";
 }
 | /* EMPTY */
 {
  @$ = "\"CURRENT\"";
 }
 ;

connection_object: name
 {
  @$ = make3_str("\"", @1, "\"");
 }
 | DEFAULT
 {
  @$ = "\"DEFAULT\"";
 }
 | char_variable
 ;

execstring: char_variable
 | CSTRING
 {
  @$ = make3_str("\"", @1, "\"");
 }
 ;

/*
 * the exec sql free command to deallocate a previously
 * prepared statement
 */
ECPGFree: SQL_FREE cursor_name
 {
  @$ = @2;
 }
 | SQL_FREE ALL
 {
  @$ = "all";
 }
 ;

/*
 * open is an open cursor, at the moment this has to be removed
 */
ECPGOpen: SQL_OPEN cursor_name opt_ecpg_using
 {
  if (@2[0] == ':')
   remove_variable_from_list(&argsinsert, find_variable(@2 + 1));
  @$ = @2;
 }
 ;

opt_ecpg_using: /* EMPTY */
 | ecpg_using
 ;

ecpg_using: USING using_list
 {
  @$ = "";
 }
 | using_descriptor
 ;

using_descriptor: USING SQL_P SQL_DESCRIPTOR quoted_ident_stringvar
 {
  add_variable_to_head(&argsinsert, descriptor_variable(@40), &no_indicator);
  @$ = "";
 }
 | USING SQL_DESCRIPTOR name
 {
  add_variable_to_head(&argsinsert, sqlda_variable(@3), &no_indicator);
  @$ = "";
 }
 ;

into_descriptor: INTO SQL_P SQL_DESCRIPTOR quoted_ident_stringvar
 {
  add_variable_to_head(&argsresult, descriptor_variable(@41), &no_indicator);
  @$ = "";
 }
 | INTO SQL_DESCRIPTOR name
 {
  add_variable_to_head(&argsresult, sqlda_variable(@3), &no_indicator);
  @$ = "";
 }
 ;

into_sqlda: INTO name
 {
  add_variable_to_head(&argsresult, sqlda_variable(@2), &no_indicator);
  @$ = "";
 }
 ;

using_list: UsingValue | UsingValue ',' using_list
 ;

UsingValue: UsingConst
 {
  char  length[32];

  snprintf(length, sizeof(length), "%zu", strlen(@1));
  add_variable_to_head(&argsinsert, new_variable(@1, ECPGmake_simple_type(ECPGt_const, length, 0), 0), &no_indicator);
 }
 | civar
 {
  @$ = "";
 }
 | civarind
 {
  @$ = "";
 }
 ;

UsingConst: Iconst
 | '+' Iconst
 | '-' Iconst
 | ecpg_fconst
 | '+' ecpg_fconst
 | '-' ecpg_fconst
 | ecpg_sconst
 | ecpg_bconst
 | ecpg_xconst
 ;

/*
 * We accept DESCRIBE [OUTPUT] but do nothing with DESCRIBE INPUT so far.
 */
ECPGDescribe: SQL_DESCRIBE INPUT_P prepared_name using_descriptor
 {
  $$.input = 1;
  $$.stmt_name = @3;
 }
 | SQL_DESCRIBE opt_output prepared_name using_descriptor
 {
  struct variable *var;

  var = argsinsert->variable;
  remove_variable_from_list(&argsinsert, var);
  add_variable_to_head(&argsresult, var, &no_indicator);

  $$.input = 0;
  $$.stmt_name = @3;
 }
 | SQL_DESCRIBE opt_output prepared_name into_descriptor
 {
  $$.input = 0;
  $$.stmt_name = @3;
 }
 | SQL_DESCRIBE INPUT_P prepared_name into_sqlda
 {
  $$.input = 1;
  $$.stmt_name = @3;
 }
 | SQL_DESCRIBE opt_output prepared_name into_sqlda
 {
  $$.input = 0;
  $$.stmt_name = @3;
 }
 ;

opt_output: SQL_OUTPUT
 | /* EMPTY */
 ;

/*
 * dynamic SQL: descriptor based access
 * originally written by Christof Petig <christof.petig@wtal.de>
 *   and Peter Eisentraut <peter.eisentraut@credativ.de>
 */

/*
 * allocate a descriptor
 */
ECPGAllocateDescr: SQL_ALLOCATE SQL_DESCRIPTOR quoted_ident_stringvar
 {
  add_descriptor(@3, connection);
  @$ = @3;
 }
 ;


/*
 * deallocate a descriptor
 */
ECPGDeallocateDescr: DEALLOCATE SQL_DESCRIPTOR quoted_ident_stringvar
 {
  drop_descriptor(@3, connection);
  @$ = @3;
 }
 ;

/*
 * manipulate a descriptor header
 */

ECPGGetDescriptorHeader: SQL_GET SQL_DESCRIPTOR quoted_ident_stringvar ECPGGetDescHeaderItems
 {
  @$ = @3;
 }
 ;

ECPGGetDescHeaderItems: ECPGGetDescHeaderItem
 | ECPGGetDescHeaderItems ',' ECPGGetDescHeaderItem
 ;

ECPGGetDescHeaderItem: cvariable '=' desc_header_item
 {
  push_assignment(@1, $3);
 }
 ;

ECPGSetDescriptorHeader: SET SQL_DESCRIPTOR quoted_ident_stringvar ECPGSetDescHeaderItems
 {
  @$ = @3;
 }
 ;

ECPGSetDescHeaderItems: ECPGSetDescHeaderItem
 | ECPGSetDescHeaderItems ',' ECPGSetDescHeaderItem
 ;

ECPGSetDescHeaderItem: desc_header_item '=' IntConstVar
 {
  push_assignment(@3, $1);
 }
 ;

IntConstVar: Iconst
 {
  char  length[32];

  snprintf(length, sizeof(length), "%zu", strlen(@1));
  new_variable(@1, ECPGmake_simple_type(ECPGt_const, length, 0), 0);
 }
 | cvariable
 ;

desc_header_item: SQL_COUNT
 {
  $$ = ECPGd_count;
 }
 ;

/*
 * manipulate a descriptor
 */

ECPGGetDescriptor: SQL_GET SQL_DESCRIPTOR quoted_ident_stringvar VALUE_P IntConstVar ECPGGetDescItems
 {
  $$.str = @5;
  $$.name = @3;
 }
 ;

ECPGGetDescItems: ECPGGetDescItem
 | ECPGGetDescItems ',' ECPGGetDescItem
 ;

ECPGGetDescItem: cvariable '=' descriptor_item
 {
  push_assignment(@1, $3);
 }
 ;

ECPGSetDescriptor: SET SQL_DESCRIPTOR quoted_ident_stringvar VALUE_P IntConstVar ECPGSetDescItems
 {
  $$.str = @5;
  $$.name = @3;
 }
 ;

ECPGSetDescItems: ECPGSetDescItem
 | ECPGSetDescItems ',' ECPGSetDescItem
 ;

ECPGSetDescItem: descriptor_item '=' AllConstVar
 {
  push_assignment(@3, $1);
 }
 ;

AllConstVar: ecpg_fconst
 {
  char  length[32];

  snprintf(length, sizeof(length), "%zu", strlen(@1));
  new_variable(@1, ECPGmake_simple_type(ECPGt_const, length, 0), 0);
 }
 | IntConstVar
 | '-' ecpg_fconst
 {
  char  length[32];
  char    *var = cat2_str("-", @2);

  snprintf(length, sizeof(length), "%zu", strlen(var));
  new_variable(var, ECPGmake_simple_type(ECPGt_const, length, 0), 0);
  @$ = var;
 }
 | '-' Iconst
 {
  char  length[32];
  char    *var = cat2_str("-", @2);

  snprintf(length, sizeof(length), "%zu", strlen(var));
  new_variable(var, ECPGmake_simple_type(ECPGt_const, length, 0), 0);
  @$ = var;
 }
 | ecpg_sconst
 {
  char  length[32];
  char    *var;

  /* Strip single quotes from ecpg_sconst */
  var = loc_strdup(@1 + 1);
  var[strlen(var) - 1] = '\0';
  snprintf(length, sizeof(length), "%zu", strlen(var));
  new_variable(var, ECPGmake_simple_type(ECPGt_const, length, 0), 0);
  @$ = var;
 }
 ;

descriptor_item: SQL_CARDINALITY  { $$ = ECPGd_cardinality; }
 | DATA_P       { $$ = ECPGd_data; }
 | SQL_DATETIME_INTERVAL_CODE  { $$ = ECPGd_di_code; }
 | SQL_DATETIME_INTERVAL_PRECISION { $$ = ECPGd_di_precision; }
 | SQL_INDICATOR      { $$ = ECPGd_indicator; }
 | SQL_KEY_MEMBER     { $$ = ECPGd_key_member; }
 | SQL_LENGTH      { $$ = ECPGd_length; }
 | NAME_P       { $$ = ECPGd_name; }
 | SQL_NULLABLE      { $$ = ECPGd_nullable; }
 | SQL_OCTET_LENGTH     { $$ = ECPGd_octet; }
 | PRECISION       { $$ = ECPGd_precision; }
 | SQL_RETURNED_LENGTH    { $$ = ECPGd_length; }
 | SQL_RETURNED_OCTET_LENGTH   { $$ = ECPGd_ret_octet; }
 | SQL_SCALE       { $$ = ECPGd_scale; }
 | TYPE_P       { $$ = ECPGd_type; }
 ;

/*
 * set/reset the automatic transaction mode, this needs a different handling
 * as the other set commands
 */
ECPGSetAutocommit: SET SQL_AUTOCOMMIT '=' on_off
 {
  @$ = @4;
 }
 | SET SQL_AUTOCOMMIT TO on_off
 {
  @$ = @4;
 }
 ;

on_off: ON
 | OFF
 ;

/*
 * set the actual connection, this needs a different handling as the other
 * set commands
 */
ECPGSetConnection: SET CONNECTION TO connection_object
 {
  @$ = @4;
 }
 | SET CONNECTION '=' connection_object
 {
  @$ = @4;
 }
 | SET CONNECTION connection_object
 {
  @$ = @3;
 }
 ;

/*
 * define a new type for embedded SQL
 */
ECPGTypedef: TYPE_P
 {
  /* reset this variable so we see if there was */
  /* an initializer specified */
  initializer = 0;
 }
 ECPGColLabel IS var_type opt_array_bounds opt_reference
 {
  add_typedef(@3, $6.index1, $6.index2, $5.type_enum, $5.type_dimension, $5.type_index, initializer, *@7 ? 1 : 0);

  if (auto_create_c == false)
   @$ = cat_str(7, "/* exec sql type", @3, "is", $5.type_str, $6.str, @7, "*/");
  else
   @$ = cat_str(6, "typedef ", $5.type_str, *@7 ? "*" : "", @3, $6.str, ";");
 }
 ;

opt_reference: SQL_REFERENCE
 | /* EMPTY */
 ;

/*
 * define the type of one variable for embedded SQL
 */
ECPGVar: SQL_VAR
 {
  /* reset this variable so we see if there was */
  /* an initializer specified */
  initializer = 0;
 }
 ColLabel IS var_type opt_array_bounds opt_reference
 {
  struct variable *p = find_variable(@3);
  const char *dimension = $6.index1;
  const char *length = $6.index2;
  struct ECPGtype *type;

  if   (($5.type_enum == ECPGt_struct ||
       $5.type_enum == ECPGt_union) &&
      initializer == 1)
   mmerror(PARSE_ERROR, ET_ERROR, "initializer not allowed in EXEC SQL VAR command");
  else
  {
   adjust_array($5.type_enum, &dimension, &length,
       $5.type_dimension, $5.type_index, *@7 ? 1 : 0, false);

   switch ($5.type_enum)
   {
     case ECPGt_struct:
     case ECPGt_union:
     if (atoi(dimension) < 0)
      type = ECPGmake_struct_type(struct_member_list[struct_level], $5.type_enum, $5.type_str, $5.type_sizeof);
     else
      type = ECPGmake_array_type(ECPGmake_struct_type(struct_member_list[struct_level], $5.type_enum, $5.type_str, $5.type_sizeof), dimension);
     break;

     case ECPGt_varchar:
     case ECPGt_bytea:
     if (atoi(dimension) == -1)
      type = ECPGmake_simple_type($5.type_enum, length, 0);
     else
      type = ECPGmake_array_type(ECPGmake_simple_type($5.type_enum, length, 0), dimension);
     break;

     case ECPGt_char:
     case ECPGt_unsigned_char:
     case ECPGt_string:
     if (atoi(dimension) == -1)
      type = ECPGmake_simple_type($5.type_enum, length, 0);
     else
      type = ECPGmake_array_type(ECPGmake_simple_type($5.type_enum, length, 0), dimension);
     break;

     default:
     if (atoi(length) >= 0)
      mmerror(PARSE_ERROR, ET_ERROR, "multidimensional arrays for simple data types are not supported");

     if (atoi(dimension) < 0)
      type = ECPGmake_simple_type($5.type_enum, "1", 0);
     else
      type = ECPGmake_array_type(ECPGmake_simple_type($5.type_enum, "1", 0), dimension);
     break;
   }

   ECPGfree_type(p->type);
   p->type = type;
  }

     @$ = cat_str(7, "/* exec sql var", @3, "is", $5.type_str, $6.str, @7, "*/");
 }
 ;

/*
 * whenever statement: decide what to do in case of error/no data found
 * according to SQL standards we lack: SQLSTATE, CONSTRAINT and SQLEXCEPTION
 */
ECPGWhenever: SQL_WHENEVER SQL_SQLERROR action
 {
  when_error.code = $3.code;
  free(when_error.command);
  when_error.command = $3.command ? mm_strdup($3.command) : NULL;
  @$ = cat_str(3, "/* exec sql whenever sqlerror ", $3.str, "; */");
 }
 | SQL_WHENEVER NOT SQL_FOUND action
 {
  when_nf.code = $4.code;
  free(when_nf.command);
  when_nf.command = $4.command ? mm_strdup($4.command) : NULL;
  @$ = cat_str(3, "/* exec sql whenever not found ", $4.str, "; */");
 }
 | SQL_WHENEVER SQL_SQLWARNING action
 {
  when_warn.code = $3.code;
  free(when_warn.command);
  when_warn.command = $3.command ? mm_strdup($3.command) : NULL;
  @$ = cat_str(3, "/* exec sql whenever sql_warning ", $3.str, "; */");
 }
 ;

action: CONTINUE_P
 {
  $$.code = W_NOTHING;
  $$.command = NULL;
  $$.str = "continue";
 }
 | SQL_SQLPRINT
 {
  $$.code = W_SQLPRINT;
  $$.command = NULL;
  $$.str = "sqlprint";
 }
 | SQL_STOP
 {
  $$.code = W_STOP;
  $$.command = NULL;
  $$.str = "stop";
 }
 | SQL_GOTO name
 {
  $$.code = W_GOTO;
  $$.command = loc_strdup(@2);
  $$.str = cat2_str("goto ", @2);
 }
 | SQL_GO TO name
 {
  $$.code = W_GOTO;
  $$.command = loc_strdup(@3);
  $$.str = cat2_str("goto ", @3);
 }
 | DO name '(' c_args ')'
 {
  $$.code = W_DO;
  $$.command = cat_str(4, @2, "(", @4, ")");
  $$.str = cat2_str("do", $$.command);
 }
 | DO SQL_BREAK
 {
  $$.code = W_BREAK;
  $$.command = NULL;
  $$.str = "break";
 }
 | DO CONTINUE_P
 {
  $$.code = W_CONTINUE;
  $$.command = NULL;
  $$.str = "continue";
 }
 | CALL name '(' c_args ')'
 {
  $$.code = W_DO;
  $$.command = cat_str(4, @2, "(", @4, ")");
  $$.str = cat2_str("call", $$.command);
 }
 | CALL name
 {
  $$.code = W_DO;
  $$.command = cat2_str(@2, "()");
  $$.str = cat2_str("call", $$.command);
 }
 ;

/* some other stuff for ecpg */

/* additional unreserved keywords */
ECPGKeywords: ECPGKeywords_vanames
 | ECPGKeywords_rest
 ;

ECPGKeywords_vanames: SQL_BREAK
 | SQL_CARDINALITY
 | SQL_COUNT
 | SQL_DATETIME_INTERVAL_CODE
 | SQL_DATETIME_INTERVAL_PRECISION
 | SQL_FOUND
 | SQL_GO
 | SQL_GOTO
 | SQL_IDENTIFIED
 | SQL_INDICATOR
 | SQL_KEY_MEMBER
 | SQL_LENGTH
 | SQL_NULLABLE
 | SQL_OCTET_LENGTH
 | SQL_RETURNED_LENGTH
 | SQL_RETURNED_OCTET_LENGTH
 | SQL_SCALE
 | SQL_SECTION
 | SQL_SQLERROR
 | SQL_SQLPRINT
 | SQL_SQLWARNING
 | SQL_STOP
 ;

ECPGKeywords_rest: SQL_CONNECT
 | SQL_DESCRIBE
 | SQL_DISCONNECT
 | SQL_OPEN
 | SQL_VAR
 | SQL_WHENEVER
 ;

/* additional keywords that can be SQL type names (but not ECPGColLabels) */
ECPGTypeName: SQL_BOOL
 | SQL_LONG
 | SQL_OUTPUT
 | SQL_SHORT
 | SQL_STRUCT
 | SQL_SIGNED
 | SQL_UNSIGNED
 ;

symbol: ColLabel
 ;

ECPGColId: ecpg_ident
 | unreserved_keyword
 | col_name_keyword
 | ECPGunreserved_interval
 | ECPGKeywords
 | ECPGCKeywords
 | CHAR_P
 | VALUES
 ;

/*
 * Name classification hierarchy.
 *
 * These productions should match those in the core grammar, except that
 * we use all_unreserved_keyword instead of unreserved_keyword, and
 * where possible include ECPG keywords as well as core keywords.
 */

/* Column identifier --- names that can be column, table, etc names.
 */
ColId: ecpg_ident
 | all_unreserved_keyword
 | col_name_keyword
 | ECPGKeywords
 | ECPGCKeywords
 | CHAR_P
 | VALUES
 ;

/* Type/function identifier --- names that can be type or function names.
 */
type_function_name: ecpg_ident
 | all_unreserved_keyword
 | type_func_name_keyword
 | ECPGKeywords
 | ECPGCKeywords
 | ECPGTypeName
 ;

/* Column label --- allowed labels in "AS" clauses.
 * This presently includes *all* Postgres keywords.
 */
ColLabel: ECPGColLabel
 | ECPGTypeName
 | CHAR_P
 | CURRENT_P
 | INPUT_P
 | INT_P
 | TO
 | UNION
 | VALUES
 | ECPGCKeywords
 | ECPGunreserved_interval
 ;

ECPGColLabel: ecpg_ident
 | unreserved_keyword
 | col_name_keyword
 | type_func_name_keyword
 | reserved_keyword
 | ECPGKeywords_vanames
 | ECPGKeywords_rest
 | CONNECTION
 ;

ECPGCKeywords: S_AUTO
 | S_CONST
 | S_EXTERN
 | S_REGISTER
 | S_STATIC
 | S_TYPEDEF
 | S_VOLATILE
 ;

/* "Unreserved" keywords --- available for use as any kind of name.
 */

/*
 * The following symbols must be excluded from ECPGColLabel and directly
 * included into ColLabel to enable C variables to get names from ECPGColLabel:
 * DAY_P, HOUR_P, MINUTE_P, MONTH_P, SECOND_P, YEAR_P.
 *
 * We also have to exclude CONNECTION, CURRENT, and INPUT for various reasons.
 * CONNECTION can be added back in all_unreserved_keyword, but CURRENT and
 * INPUT are reserved for ecpg purposes.
 *
 * The mentioned exclusions are done by $replace_line settings in parse.pl.
 */
all_unreserved_keyword: unreserved_keyword
 | ECPGunreserved_interval
 | CONNECTION
 ;

ECPGunreserved_interval: DAY_P
 | HOUR_P
 | MINUTE_P
 | MONTH_P
 | SECOND_P
 | YEAR_P
 ;

into_list: coutputvariable | into_list ',' coutputvariable
 ;

ecpgstart: SQL_START
 {
  reset_variables();
  pacounter = 1;
  @$ = "";
 }
 ;

c_args: /* EMPTY */
 | c_list
 ;

coutputvariable: cvariable indicator
 {
  add_variable_to_head(&argsresult, find_variable(@1), find_variable(@2));
 }
 | cvariable
 {
  add_variable_to_head(&argsresult, find_variable(@1), &no_indicator);
 }
 ;


civarind: cvariable indicator
 {
  if (find_variable(@2)->type->type == ECPGt_array)
   mmerror(PARSE_ERROR, ET_ERROR, "arrays of indicators are not allowed on input");

  add_variable_to_head(&argsinsert, find_variable(@1), find_variable(@2));
  @$ = create_questionmarks(@1, false);
 }
 ;

char_civar: char_variable
 {
  char    *ptr = (char *) strstr(@1, ".arr");

  if (ptr)    /* varchar, we need the struct name here, not
         * the struct element */
   *ptr = '\0';
  add_variable_to_head(&argsinsert, find_variable(@1), &no_indicator);
 }
 ;

civar: cvariable
 {
  add_variable_to_head(&argsinsert, find_variable(@1), &no_indicator);
  @$ = create_questionmarks(@1, false);
 }
 ;

indicator: cvariable
 {
  check_indicator((find_variable(@1))->type);
 }
 | SQL_INDICATOR cvariable
 {
  check_indicator((find_variable(@2))->type);
  @$ = @2;
 }
 | SQL_INDICATOR name
 {
  check_indicator((find_variable(@2))->type);
  @$ = @2;
 }
 ;

cvariable: CVARIABLE
 {
  /*
   * As long as multidimensional arrays are not implemented we have to
   * check for those here
   */
  const char *ptr = @1;
  int   brace_open = 0,
     brace = false;

  for (; *ptr; ptr++)
  {
   switch (*ptr)
   {
    case '[':
     if (brace)
      mmfatal(PARSE_ERROR, "multidimensional arrays for simple data types are not supported");
     brace_open++;
     break;
    case ']':
     brace_open--;
     if (brace_open == 0)
      brace = true;
     break;
    case '\t':
    case ' ':
     break;
    default:
     if (brace_open == 0)
      brace = false;
     break;
   }
  }
 }
 ;

ecpg_param: PARAM
 ;

ecpg_bconst: BCONST
 ;

ecpg_fconst: FCONST
 ;

ecpg_sconst: SCONST
 ;

ecpg_xconst: XCONST
 ;

ecpg_ident: IDENT
 | CSTRING
 {
  @$ = make3_str("\"", @1, "\"");
 }
 ;

quoted_ident_stringvar: name
 {
  @$ = make3_str("\"", @1, "\"");
 }
 | char_variable
 {
  @$ = make3_str("(", @1, ")");
 }
 ;

/*
 * C stuff
 */

c_stuff_item: c_anything
 | '(' ')'
 {
  @$ = "()";
 }
 | '(' c_stuff ')'
 ;

c_stuff: c_stuff_item
 | c_stuff c_stuff_item
 ;

c_list: c_term
 | c_list ',' c_term
 ;

c_term: c_stuff
 | '{' c_list '}'
 ;

c_thing: c_anything
 | '('
 | ')'
 | ','
 | ';'
 ;

/*
 * Note: NULL_P is treated specially to force it to be output in upper case,
 * since it's likely meant as a reference to the standard C macro NULL.
 */
c_anything: ecpg_ident
 | Iconst
 | ecpg_fconst
 | ecpg_sconst
 | '*'
 | '+'
 | '-'
 | '/'
 | '%'
 | NULL_P      { @$ = "NULL"; }
 | S_ADD
 | S_AND
 | S_ANYTHING
 | S_AUTO
 | S_CONST
 | S_DEC
 | S_DIV
 | S_DOTPOINT
 | S_EQUAL
 | S_EXTERN
 | S_INC
 | S_LSHIFT
 | S_MEMBER
 | S_MEMPOINT
 | S_MOD
 | S_MUL
 | S_NEQUAL
 | S_OR
 | S_REGISTER
 | S_RSHIFT
 | S_STATIC
 | S_SUB
 | S_TYPEDEF
 | S_VOLATILE
 | SQL_BOOL
 | ENUM_P
 | HOUR_P
 | INT_P
 | SQL_LONG
 | MINUTE_P
 | MONTH_P
 | SECOND_P
 | SQL_SHORT
 | SQL_SIGNED
 | SQL_STRUCT
 | SQL_UNSIGNED
 | YEAR_P
 | CHAR_P
 | FLOAT_P
 | TO
 | UNION
 | VARCHAR
 | '['
 | ']'
 | '='
 | ':'
 ;

DeallocateStmt: DEALLOCATE prepared_name
 {
  check_declared_list(@2);
  @$ = @2;
 }
 | DEALLOCATE PREPARE prepared_name
 {
  check_declared_list(@3);
  @$ = @3;
 }
 | DEALLOCATE ALL
 {
  @$ = "all";
 }
 | DEALLOCATE PREPARE ALL
 {
  @$ = "all";
 }
 ;

Iresult: Iconst
 | '(' Iresult ')'
 | Iresult '+' Iresult
 | Iresult '-' Iresult
 | Iresult '*' Iresult
 | Iresult '/' Iresult
 | Iresult '%' Iresult
 | ecpg_sconst
 | ColId
 | ColId '(' var_type ')'
 {
  if (pg_strcasecmp(@1, "sizeof") != 0)
   mmerror(PARSE_ERROR, ET_ERROR, "operator not allowed in variable definition");
  else
   @$ = cat_str(4, @1, "(", $3.type_str, ")");
 }
 ;

execute_rest: /* EMPTY */
 | ecpg_using opt_ecpg_into
 | ecpg_into ecpg_using
 | ecpg_into
 ;

ecpg_into: INTO into_list
 {
  /* always suppress this from the constructed string */
  @$ = "";
 }
 | into_descriptor
 ;

opt_ecpg_into: /* EMPTY */
 | ecpg_into
 ;

ecpg_fetch_into: ecpg_into
 | using_descriptor
 {
  struct variable *var;

  var = argsinsert->variable;
  remove_variable_from_list(&argsinsert, var);
  add_variable_to_head(&argsresult, var, &no_indicator);
 }
 ;

opt_ecpg_fetch_into: /* EMPTY */
 | ecpg_fetch_into
 ;

%%

void
base_yyerror(const char *error)
{
 /* translator: %s is typically the translation of "syntax error" */
 mmerror(PARSE_ERROR, ET_ERROR, "%s at or near \"%s\"",
   _(error), token_start ? token_start : base_yytext);
}

void
parser_init(void)
{
 /*
  * This function is empty. It only exists for compatibility with the
  * backend parser right now.
  */
}

[Dauer der Verarbeitung: 0.28 Sekunden, vorverarbeitet 2026-08-08]