Disclaimer: First, I'm aware that this is C++ code and this is a C# forum, I'm not all that familiar with C++ and was hoping someone here could explain the example below in C#.

I was browsing the very complex source code of the open source project MySQL and saw this bit of code (see attachment for full file, original extension was cc, not cpp):

/* A structure which contains information on the commands this program
   can understand. */

typedef struct {
  const char *name;		/* User printable name of the function. */
  char cmd_char;		/* msql command character */
  int (*func)(String *str,char *); /* Function to call to do the job. */
  bool takes_params;		/* Max parameters for command */
  const char *doc;		/* Documentation for this function.  */
} COMMANDS;

I understand everything except for line 7:

int (*func)(String *str,char *); /* Function to call to do the job. */

The code moves on immediately after in this type of fashion:

static COMMANDS commands[] = {
  { "?",      '?', com_help,   1, "Synonym for `help'." },
  { "clear",  'c', com_clear,  0, "Clear command."},
  { "connect",'r', com_connect,1,
/* SNIPPED FOR BREVITY */
  { "lock tables",      0, 0, 0, ""},
  { "unlock tables",    0, 0, 0, ""},
  /* generated 2006-12-28.  Refresh occasionally from lexer. */
  { "ACTION", 0, 0, 0, ""},
  { "ADD", 0, 0, 0, ""},
  { "AFTER", 0, 0, 0, ""},
  { "AGAINST", 0, 0, 0, ""},
  { "AGGREGATE", 0, 0, 0, ""},
  { "ALL", 0, 0, 0, ""},
  { "ALGORITHM", 0, 0, 0, ""},
  { "ALTER", 0, 0, 0, ""},
  { "ANALYZE", 0, 0, 0, ""},
  { "AND", 0, 0, 0, ""},
  { "ANY", 0, 0, 0, ""},
  { "AS", 0, 0, 0, ""},
  { "ASC", 0, 0, 0, ""},
/* ETC... */
  { "WEEKDAY", 0, 0, 0, ""},
  { "WEEKOFYEAR", 0, 0, 0, ""},
  { "WITHIN", 0, 0, 0, ""},
  { "X", 0, 0, 0, ""},
  { "Y", 0, 0, 0, ""},
  { "YEARWEEK", 0, 0, 0, ""},
  /* end sentinel */
  { (char *)NULL,       0, 0, 0, ""}
};

Even though I don't understand line 7, why would anyone waste time creating a struct like this if everything (seriously over 100 lines) all contain 99% of the same data. Am I missing something? Can anyone shed any light here, or even throw out a guess?

Edited 5 Years Ago by zachattack05: n/a

Attachments
/* Copyright (C) 2000-2008 MySQL AB

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

/* mysql command tool
 * Commands compatible with mSQL by David J. Hughes
 *
 * Written by:
 *   Michael 'Monty' Widenius
 *   Andi Gutmans  <andi@zend.com>
 *   Zeev Suraski  <zeev@zend.com>
 *   Jani Tolonen  <jani@mysql.com>
 *   Matt Wagner   <matt@mysql.com>
 *   Jeremy Cole   <jcole@mysql.com>
 *   Tonu Samuel   <tonu@mysql.com>
 *   Harrison Fisk <harrison@mysql.com>
 *
 **/

#include "client_priv.h"
#include <m_ctype.h>
#include <stdarg.h>
#include <my_dir.h>
#ifndef __GNU_LIBRARY__
#define __GNU_LIBRARY__		      // Skip warnings in getopt.h
#endif
#include "my_readline.h"
#include <signal.h>
#include <violite.h>

#if defined(USE_LIBEDIT_INTERFACE) && defined(HAVE_LOCALE_H)
#include <locale.h>
#endif

const char *VER= "14.15";

/* Don't try to make a nice table if the data is too big */
#define MAX_COLUMN_LENGTH	     1024

/* Buffer to hold 'version' and 'version_comment' */
static char *server_version= NULL;

/* Array of options to pass to libemysqld */
#define MAX_SERVER_ARGS               64

void* sql_alloc(unsigned size);	     // Don't use mysqld alloc for these
void sql_element_free(void *ptr);
#include "sql_string.h"

extern "C" {
#if defined(HAVE_CURSES_H) && defined(HAVE_TERM_H)
#include <curses.h>
#include <term.h>
#else
#if defined(HAVE_TERMIOS_H)
#include <termios.h>
#include <unistd.h>
#elif defined(HAVE_TERMBITS_H)
#include <termbits.h>
#elif defined(HAVE_ASM_TERMBITS_H) && (!defined __GLIBC__ || !(__GLIBC__ > 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ > 0))
#include <asm/termbits.h>		// Standard linux
#endif
#undef VOID
#if defined(HAVE_TERMCAP_H)
#include <termcap.h>
#else
#ifdef HAVE_CURSES_H
#include <curses.h>
#endif
#undef SYSV				// hack to avoid syntax error
#ifdef HAVE_TERM_H
#include <term.h>
#endif
#endif
#endif

#undef bcmp				// Fix problem with new readline
#if defined( __WIN__)
#include <conio.h>
#elif !defined(__NETWARE__)
#include <readline/readline.h>
#define HAVE_READLINE
#endif
  //int vidattr(long unsigned int attrs);	// Was missing in sun curses
}

#if !defined(HAVE_VIDATTR)
#undef vidattr
#define vidattr(A) {}			// Can't get this to work
#endif

#ifdef FN_NO_CASE_SENCE
#define cmp_database(cs,A,B) my_strcasecmp((cs), (A), (B))
#else
#define cmp_database(cs,A,B) strcmp((A),(B))
#endif

#if !defined( __WIN__) && !defined(__NETWARE__) && !defined(THREAD)
#define USE_POPEN
#endif

#include "completion_hash.h"

#define PROMPT_CHAR '\\'
#define DEFAULT_DELIMITER ";"

#define MAX_BATCH_BUFFER_SIZE (1024L * 1024L)

typedef struct st_status
{
  int exit_status;
  ulong query_start_line;
  char *file_name;
  LINE_BUFFER *line_buff;
  bool batch,add_to_history;
} STATUS;


static HashTable ht;
static char **defaults_argv;

enum enum_info_type { INFO_INFO,INFO_ERROR,INFO_RESULT};
typedef enum enum_info_type INFO_TYPE;

static MYSQL mysql;			/* The connection */
static my_bool ignore_errors=0,wait_flag=0,quick=0,
               connected=0,opt_raw_data=0,unbuffered=0,output_tables=0,
	       opt_rehash=1,skip_updates=0,safe_updates=0,one_database=0,
	       opt_compress=0, using_opt_local_infile=0,
	       vertical=0, line_numbers=1, column_names=1,opt_html=0,
               opt_xml=0,opt_nopager=1, opt_outfile=0, named_cmds= 0,
	       tty_password= 0, opt_nobeep=0, opt_reconnect=1,
	       default_charset_used= 0, opt_secure_auth= 0,
               default_pager_set= 0, opt_sigint_ignore= 0,
               auto_vertical_output= 0,
               show_warnings= 0, executing_query= 0, interrupted_query= 0;
static my_bool debug_info_flag, debug_check_flag, batch_abort_on_error;
static my_bool column_types_flag;
static my_bool preserve_comments= 0;
static ulong opt_max_allowed_packet, opt_net_buffer_length;
static uint verbose=0,opt_silent=0,opt_mysql_port=0, opt_local_infile=0;
static uint my_end_arg;
static char * opt_mysql_unix_port=0;
static int connect_flag=CLIENT_INTERACTIVE;
static char *current_host,*current_db,*current_user=0,*opt_password=0,
            *current_prompt=0, *delimiter_str= 0,
            *default_charset= (char*) MYSQL_DEFAULT_CHARSET_NAME;
static char *histfile;
static char *histfile_tmp;
static String glob_buffer,old_buffer;
static String processed_prompt;
static char *full_username=0,*part_username=0,*default_prompt=0;
static int wait_time = 5;
static STATUS status;
static ulong select_limit,max_join_size,opt_connect_timeout=0;
static char mysql_charsets_dir[FN_REFLEN+1];
static const char *xmlmeta[] = {
  "&", "&amp;",
  "<", "&lt;",
  ">", "&gt;",
  "\"", "&quot;",
  0, 0
};
static const char *day_names[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
static const char *month_names[]={"Jan","Feb","Mar","Apr","May","Jun","Jul",
			    "Aug","Sep","Oct","Nov","Dec"};
static char default_pager[FN_REFLEN];
static char pager[FN_REFLEN], outfile[FN_REFLEN];
static FILE *PAGER, *OUTFILE;
static MEM_ROOT hash_mem_root;
static uint prompt_counter;
static char delimiter[16]= DEFAULT_DELIMITER;
static uint delimiter_length= 1;
unsigned short terminal_width= 80;

#ifdef HAVE_SMEM
static char *shared_memory_base_name=0;
#endif
static uint opt_protocol=0;
static CHARSET_INFO *charset_info= &my_charset_latin1;

#include "sslopt-vars.h"

const char *default_dbug_option="d:t:o,/tmp/mysql.trace";

void tee_fprintf(FILE *file, const char *fmt, ...);
void tee_fputs(const char *s, FILE *file);
void tee_puts(const char *s, FILE *file);
void tee_putc(int c, FILE *file);
static void tee_print_sized_data(const char *, unsigned int, unsigned int, bool);
/* The names of functions that actually do the manipulation. */
static int get_options(int argc,char **argv);
extern "C" my_bool get_one_option(int optid, const struct my_option *opt,
                                  char *argument);
static int com_quit(String *str,char*),
	   com_go(String *str,char*), com_ego(String *str,char*),
	   com_print(String *str,char*),
	   com_help(String *str,char*), com_clear(String *str,char*),
	   com_connect(String *str,char*), com_status(String *str,char*),
	   com_use(String *str,char*), com_source(String *str, char*),
	   com_rehash(String *str, char*), com_tee(String *str, char*),
           com_notee(String *str, char*), com_charset(String *str,char*),
           com_prompt(String *str, char*), com_delimiter(String *str, char*),
     com_warnings(String *str, char*), com_nowarnings(String *str, char*);

#ifdef USE_POPEN
static int com_nopager(String *str, char*), com_pager(String *str, char*),
           com_edit(String *str,char*), com_shell(String *str, char *);
#endif

static int read_and_execute(bool interactive);
static int sql_connect(char *host,char *database,char *user,char *password,
		       uint silent);
static const char *server_version_string(MYSQL *mysql);
static int put_info(const char *str,INFO_TYPE info,uint error=0,
		    const char *sql_state=0);
static int put_error(MYSQL *mysql);
static void safe_put_field(const char *pos,ulong length);
static void xmlencode_print(const char *src, uint length);
static void init_pager();
static void end_pager();
static void init_tee(const char *);
static void end_tee();
static const char* construct_prompt();
static char *get_arg(char *line, my_bool get_next_arg);
static void init_username();
static void add_int_to_prompt(int toadd);
static int get_result_width(MYSQL_RES *res);
static int get_field_disp_length(MYSQL_FIELD * field);

/* A structure which contains information on the commands this program
   can understand. */

typedef struct {
  const char *name;		/* User printable name of the function. */
  char cmd_char;		/* msql command character */
  int (*func)(String *str,char *); /* Function to call to do the job. */
  bool takes_params;		/* Max parameters for command */
  const char *doc;		/* Documentation for this function.  */
} COMMANDS;

static COMMANDS commands[] = {
  { "?",      '?', com_help,   1, "Synonym for `help'." },
  { "clear",  'c', com_clear,  0, "Clear command."},
  { "connect",'r', com_connect,1,
    "Reconnect to the server. Optional arguments are db and host." },
  { "delimiter", 'd', com_delimiter,    1,
    "Set statement delimiter." },
#ifdef USE_POPEN
  { "edit",   'e', com_edit,   0, "Edit command with $EDITOR."},
#endif
  { "ego",    'G', com_ego,    0,
    "Send command to mysql server, display result vertically."},
  { "exit",   'q', com_quit,   0, "Exit mysql. Same as quit."},
  { "go",     'g', com_go,     0, "Send command to mysql server." },
  { "help",   'h', com_help,   1, "Display this help." },
#ifdef USE_POPEN
  { "nopager",'n', com_nopager,0, "Disable pager, print to stdout." },
#endif
  { "notee",  't', com_notee,  0, "Don't write into outfile." },
#ifdef USE_POPEN
  { "pager",  'P', com_pager,  1, 
    "Set PAGER [to_pager]. Print the query results via PAGER." },
#endif
  { "print",  'p', com_print,  0, "Print current command." },
  { "prompt", 'R', com_prompt, 1, "Change your mysql prompt."},
  { "quit",   'q', com_quit,   0, "Quit mysql." },
  { "rehash", '#', com_rehash, 0, "Rebuild completion hash." },
  { "source", '.', com_source, 1,
    "Execute an SQL script file. Takes a file name as an argument."},
  { "status", 's', com_status, 0, "Get status information from the server."},
#ifdef USE_POPEN
  { "system", '!', com_shell,  1, "Execute a system shell command."},
#endif
  { "tee",    'T', com_tee,    1, 
    "Set outfile [to_outfile]. Append everything into given outfile." },
  { "use",    'u', com_use,    1,
    "Use another database. Takes database name as argument." },
  { "charset",    'C', com_charset,    1,
    "Switch to another

Line 7 is a reference to a function that takes a pointer to a string, a pointer to a character and returns an integer. In C# we call these delegates.

It looks to be some form of SQL parser.

Hmm...I don't get how they are constructing it then.

In the example above, the first code snippet on line 2 they set that value to "com_help" and then on line 6 and on it is set to "0"

I understand now that it's a delegate, but I don't get how you can set that field to either a string (which I'm assuming points to the name of the function to call) or to an integer. I understand that the function is supposed to return an integer, but I wasn't aware that you could just skip calling a function and just return 0.

Maybe I don't fully understand.

I must say, as a side note, and no offense to anyone who may have contributed, but that source code has got to be the most disorganized mess I've ever read. I got the biggest headache just trying to locate references. But then again I'm not a C++ programmer so meh.

In line 2 they set it to com_help which is a function (not a string). So when it is compiled it puts the address of com_help in that spot. One thing to note about C/C++ is that pointers (which this is) are integer types. They can be set to any number you like, and 0 usually means 'no pointer' (or void *). If you are trying to learn C/C++ just wait until you get to pointer math. Or pointer pointers.

Yes, the code is messy :)

Edited 5 Years Ago by Momerath: n/a

I'm not necessarily trying to learn C/C++, my current project is in C# and that's where it will remain until it is finished, maybe after I'll consider learning C/C++.

The reason I was even looking at this code was to find out how the SQL server processed commands from clients...and even now, after hours of reading, browsing (literally) thousands of files and consuming more aspirin than should legally be allowed I still don't know how it's done.

"COMMANDS" is mis-named. It's a wrapper for a single command. The long list of similar statements in your 3rd snippet populates an array with a whole pile of commands, one per line. The first couple look sensible: the command "clear" has the command character "c", calls com_clear, takes 0 parameters and has the documentation string "Clear command." So far so good. But then there are a whole pile that have a name but no associated action or documentation so it seems that they don't do anything except occupy memory. I think the clue is in the comment "generated 2006-12-28. Refresh occasionally from lexer." This stuff is all machine generated. The lexer knows those keywords, so they're in the list, but no action is associated with them so they're all set to no command character, no action, no parameters, no documentation string.

digitig,

Is "lexer" something that is supposed to be common knowledge that I am oblivious to? What I mean is, is it something specific to this application or is it a function of some sort, a website, service...something?

"Lexer" is a standard thing, but it's only common knowledge to those who get involved with turning plain text into computer representation. It's the first step in the process, which breaks the text into individual words that the program will hopefully recognise. Look up the documentation on the Unix utility "Lex" (or the gnu version "Flex").

I see now.

I'm assuming then that the "commands" that have no function to call, only the keyword is set...the "command" that is performed when that keyword is used is generated by Lexer, and not the programmer?

I guess what I mean is, when the command "ADD" is sent (from the example above), the code that is executed is generated by Lexer, not the programmer that created the COMMANDS array? Though I guess they COULD be the same person, but...it's something external is what I'm getting at.

Is that right?

Generally, something like Lex takes a set of rules and actions. It then parses the input it gets based on the rules and performs the actions. A lot of times the actions are left out so that the user of Lex can then use the generated code in YACC (or it's like) to create a compiler or interpreter of the commands.

Since you said this had something to do with parsing SQL commands, I suspect there is another module somewhere that is using this as a parser of SQL commands then performing actions based on the parsed output.

Meh, you lost me...it's really not important at the moment I guess. It was more of a curiosity thing.

Thanks for trying to explain it :)

Code Snip, Also Know as a Code Snippet. Is A small Piece of Code That Can be used in Different Applications To Get A result. There is No Way to Know what the snippet was intentionnaly ment for it could be used for many things as to extract certain data from an sql database and so forth like this snippet that i use alot in c# which is not as elaborate as the one shown above.

private string getName(OpenFileDialog dlg)
{
string s = "";
string[] name = dlg.FileName.Split('\\');
for (int i=0;i<name.Length;i++)
{
s = name;
}
return s;
}

This question has already been answered. Start a new discussion instead.