Getting number of columns in a table using ' Proc C-C`` - c++

I am using the below code to get the number of columns in an oracle table.
char selectQuery[30000] = {'\0'};
strcpy(selectQuery, "SELECT COUNT(*) FROM USER_TAB_COLUMNS WHERE TABLE_NAME=\'");
strcat(selectQuery, tableName);
strcat(selectQuery, "\'");
strcpy((char*) stmt.arr, selectQuery);
stmt.len = strlen((char*) stmt.arr );
stmt.arr[stmt.len]= '\0';
EXEC SQL WHENEVER SQLERROR CONTINUE;
EXEC SQL WHENEVER NOT FOUND CONTINUE;
EXEC SQL DECLARE SELECTCOLNU STATEMENT;
EXEC SQL PREPARE SELECTCOLNU FROM :stmt;
if(sqlca.sqlcode != 0)
{
DEBUG_LOG("SQL-ERR:Preparation of SELECT Query to get number of columns failed: Ora-Err: %d %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
return PREPARATION_FAILURE;
}
EXEC SQL EXECUTE SELECTCOLNU INTO:columnsNo;
if(sqlca.sqlcode < 0)
{
DEBUG_LOG("SQL-ERR:Execute failed: Ora-Err: %d %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
return EXECTUION_FAILURE;
}
DEBUG_LOG("Number of columns: %d\n", columnsNo);
When I execute the code, It doesn't give any error but I am getting "Number of columns: 0" as the output.
There are few columns in the table I am referring.
Am I doing anything wrong here?
Below is the declaration section...
EXEC SQL BEGIN DECLARE SECTION;
int columnsNo;
VARCHAR stmt[MAX_SQL];
EXEC SQL END DECLARE SECTION;

Don't "escape" the ' in a C- string. It will have \' just in the string, and that is not what you want because the ' is the database string quote, which you now escape for the database and the database doesn't understand the query now.
sprintf(selectQuery, "SELECT COUNT(*) FROM USER_TAB_COLUMNS WHERE TABLE_NAME='%s'", tableName);
Note:
stmt.len = strlen((char*) stmt.arr );
stmt.arr[stmt.len]= '\0';
In the above strlen counts the number of characters until a null character. Thus stmt.arr[stmt.len] is already null. (No harm, though.)

Related

WHERE column = value, only work with INTEGER value

I use sqlite on a c++ project, but I have a problem when i use WHERE on a column with TEXT values
I created a sqlite database:
CREATE TABLE User( id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(24))
When i try to get the value of the column with VARCHAR values, it doesn't work, and return me a STATUS_CODE 101 just after the sqlite3_step :
int res = 0;
sqlite3_stmt *request;
char *sqlSelection = (char *)"SELECT * FROM User WHERE name='bob' ";
int id = 0;
res = sqlite3_prepare_v2(db, sqlSelection, strlen(sqlSelection), &request, NULL);
if (!res){
while (res == SQLITE_OK || res == SQLITE_ROW){
res = sqlite3_step(request);
if (res == SQLITE_OK || res == SQLITE_ROW ){
id = sqlite3_column_int(request, 0);
printf("User exist %i \n",id);
}
}
sqlite3_finalize(request);
I also tried with LIKE but it also doesn't work
SELECT * FROM User WHERE name LIKE '%bob%'
But when I execute the same code but for an INTERGER value
SELECT * FROM User WHERE id=1
It work fine.
In DB Browser for SQLite all requests work fine.
To solve the problem I searched what status code 101 means.
Here is what they said.
(101) SQLITE_DONE
The SQLITE_DONE result code indicates that an operation has completed.
The SQLITE_DONE result code is most commonly seen as a return value
from sqlite3_step() indicating that the SQL statement has run to
completion. But SQLITE_DONE can also be returned by other multi-step
interfaces such as sqlite3_backup_step().
https://sqlite.org/rescode.html
So, you're getting 101 because there is no more result from SELECT SQL.
The solution was to replace the VARCHAR fields by TEXT.
SQLite for c++ seems to don't manage VARCHAR fields when they are used after the WHERE

SQLite Update table

Trying to update table by user specified values. But the values are not getting updated.
cout<<"\nEnter Ac No"<<endl;
cin>>ac;
cout<<"\nEnter Amount"<<endl;
cin>>amt;
/* Create merged SQL statement */
sql = "UPDATE RECORDS set BAL = '%d' where ACCOUNT_NO = '%d'",amt, ac;
/* Execute SQL statement */
rc = sqlite3_exec(db, sql, callback, (void*)data, &zErrMsg);
If I replace BAL and ACCOUNT_NO by some integer value instead of place holder then it is working fine.
Your sql string is not being created properly.
If you expect this code
sql = "UPDATE RECORDS set BAL = '%d' where ACCOUNT_NO = '%d'",amt, ac;
to result in
"UPDATE RECORDS set BAL = '1' where ACCOUNT_NO = '2'"
where
amt= 1 and ac = 2 then you need to use a string formatting call like this.
// the buffer where your sql statement will live
char sql[1024];
// write the SQL statment with values into the buffer
_snprintf(sql,sizeof(sql)-1, "UPDATE RECORDS set BAL = '%d' where ACCOUNT_NO = '%d'",amt, ac);
buff[sizeof(sql)-1]='\0';
On your particular platform _snprintf(...) might be snprintf(..) or another similarly named function. Also your compiler may warn about buffer manipulation security vulnerabilities. Choose the appropriate substitute for your needs

How to Compare my sql columns dynamically

I am not able to get idea about the following requirement. The example table follows.
CREATE TABLE `test` (
`Id` INT NOT NULL,
`Name` VARCHAR(45) NULL,
`did_fk` INT NULL,
`adid_fk` INT NULL,
PRIMARY KEY (`Id`));
INSERT INTO test (id,name,did_fk,adid_fk)
VALUES
(1,'Rajesh',1,1),
(2,'Neeli',2,2),
(3,'Satish',3,3),
(4,'Ganesh',4,5),
(5,'Murali',9,10);
Here I need to compare the "id" with _fk columns i.e. did_fk & adid_fk. The "id" should be equal to did_fk & as well as adid_fk. If any of them is not true, then I should get that row.Here I need to get the rows 4 & 5.Since "_fk" columns are not equal to "id" value.Problem is "_fk" columns are not fixed. But "id" name is fixed.
SELECT * FROM `test` WHERE `Id` != `did_fk` OR `Id` != `adid_fk`
If your dynamic columns ends with _fk or some another suffix you can try to create SP like following
CREATE DEFINER=`root`#`localhost` PROCEDURE `GetNonEqualFkValues`(IN tableName varchar(255))
BEGIN
DECLARE c_name VARCHAR(255);
DECLARE done INT DEFAULT FALSE;
DECLARE curs CURSOR FOR select column_name from information_schema.columns where column_name like '%_fk';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN curs;
SET #q = concat("SELECT * FROM ", tableName, " WHERE 1!=1 ");
get_col: LOOP
FETCH curs INTO c_name;
IF done THEN
LEAVE get_col;
END IF;
SET #q = CONCAT(#q, " OR ", c_name," != id");
END LOOP get_col;
PREPARE stmt1 FROM #q;
EXECUTE stmt1;
END
And then invoke for concrete table like
call GetNonEqualFkValues('test')
The code isn't perfect, but it works for me and I think idea should be clear.

For MySQLDump, add a comment which outputs how many rows were dumped for each table

I would like to add a comment which outputs the number of rows that were dumped for each table, for MySQLDump. For this I need to modify the file client/mysqldump.c. An example output could be like this:
-- Table structure for table t1
DROP TABLE IF EXISTS `t1`;
/*!40101 SET #saved_cs_client = ##character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `t1` (
`id` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = #saved_cs_client */;
--
-- Dumping data for table t1
LOCK TABLES `t1` WRITE;
/*!40000 ALTER TABLE `t1` DISABLE KEYS */;
INSERT INTO `t1` VALUES (5);
/*!40000 ALTER TABLE `t1` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=#OLD_TIME_ZONE */;
-- Rows found for 't1': 1
I haven't used MySQL much and don't understand how should I proceed towards solving this problem. Any help would be appreciated. Thanks!
The MYSQL C API has a function that return the number of rows returned by your request mysql_num_rows (http://dev.mysql.com/doc/refman/5.0/en/mysql-num-rows.html).
You can use it with the request:
SELECT * FROM MY_TABLE;
And you got the number of rows in your table.
Here a sample code:
MYSQL * myh
MYSQL_RES *query_result;
unsigned long table_num_rows;
/* Select all records present in the table */
if (0 != mysql_query(myh, "SELECT * FROM MY_TABLE"))
{
fprintf(stderr, "FAIL to perform the query : 'SELECT * FROM MY_TABLE' %s\n", mysql_error(myh));
exit (EXIT_FAILURE);
}
query_result = mysql_store_result(myh);
if (query_result)
{
/* Retreive the number of rows returned by the query, which is the total number of rows in the table
* in our case.
*/
table_num_rows = mysql_num_rows(query_result);
fprintf(stdout, "Our table contain %lu\n", table_num_rows)
}
For a complete example on how to use the MYSQL C API, you can read the answer here:
Writing into mysql database from a single board computer using c
EDIT : It seems that the function dump_table is used when we want to dump any table (mysqldump.c), it's a good place to add our code to count the number of rows. Modify it like this (You need to do some tests, i have not tested the code on my machine!):
static void dump_table(char *table, char *db)
{
char ignore_flag;
char buf[200], table_buff[NAME_LEN+3];
DYNAMIC_STRING query_string;
char table_type[NAME_LEN];
char *result_table, table_buff2[NAME_LEN*2+3], *opt_quoted_table;
int error= 0;
ulong rownr, row_break, total_length, init_length;
uint num_fields;
MYSQL_RES *res;
MYSQL_RES *res_num_row; /* Add this */
MYSQL_FIELD *field;
MYSQL_ROW row;
char select_expr[QUERY_LENGTH];
unsigned long table_num_rows; /* Add this */
char table_num_rows_query[256]; /* Add this */
DBUG_ENTER("dump_table");
/* Add this */
/* Build the query to get the number of rows */
snprintf(table_num_rows_query, 256, "SELECT * FROM %s", table);
/*
* Make sure you get the create table info before the following check for
* --no-data flag below. Otherwise, the create table info won't be printed.
* */
num_fields= get_table_structure(table, db, table_type, &ignore_flag);
/*
* The "table" could be a view. If so, we don't do anything here.
* */
if (strcmp(table_type, "VIEW") == 0)
DBUG_VOID_RETURN;
/* Check --no-data flag */
if (opt_no_data)
{
verbose_msg("-- Skipping dump data for table '%s', --no-data was used\n",
table);
DBUG_VOID_RETURN;
}
DBUG_PRINT("info",
("ignore_flag: %x num_fields: %d", (int) ignore_flag,
num_fields));
/*
* If the table type is a merge table or any type that has to be
* _completely_ ignored and no data dumped
* */
if (ignore_flag & IGNORE_DATA)
{
verbose_msg("-- Warning: Skipping data for table '%s' because " \
"it's of type %s\n", table, table_type);
DBUG_VOID_RETURN;
}
/* Check that there are any fields in the table */
if (num_fields == 0)
{
verbose_msg("-- Skipping dump data for table '%s', it has no fields\n",
table);
DBUG_VOID_RETURN;
}
/*
* Check --skip-events flag: it is not enough to skip creation of events
* discarding SHOW CREATE EVENT statements generation. The myslq.event
* table data should be skipped too.
*/
if (!opt_events && !my_strcasecmp(&my_charset_latin1, db, "mysql") &&
!my_strcasecmp(&my_charset_latin1, table, "event"))
{
verbose_msg("-- Skipping data table mysql.event, --skip-events was used\n");
DBUG_VOID_RETURN;
}
result_table= quote_name(table,table_buff, 1);
opt_quoted_table= quote_name(table, table_buff2, 0);
if (opt_lossless_fp && get_select_expr(table, select_expr))
exit(EX_MYSQLERR);
verbose_msg("-- Sending SELECT query...\n");
/* Add this */
/* TODO : check if this is the right place to put our request */
if (0 != mysql_query(mysql, table_num_rows_query))
{
fprintf(stderr, "FAIL to perform the query : %s - %s\n", table_num_rows_query, mysql_error(myh));
exit (EXIT_FAILURE);
}
res_num_row = mysql_store_result(mysql);
if (res_num_row)
{
/* Retreive the number of rows returned by the query, which is the total number of rows in the table
* in our case.
*/
table_num_rows = mysql_num_rows(res_num_row);
fprintf(stdout, "Our table contain %lu\n", table_num_rows);
}
/* Freeing the result */
mysql_free_result(res_num_row);
init_dynamic_string_checked(&query_string, "", 1024, 1024);
/* The rest of the function here */

SQLite SELECT JOIN and VIEWS in C++

I have a two tables in SQLITE and View where I try to compine these two tables (my idea is to get all the rows from table1 where table1.field1 and table2.field99 have the same value.
Tables and View are Table1,Table2 and View1
View code is "SELECT * FROM Table1 JOIN Table2 ON UPPER(Field1) LIKE UPPER(Table2.Field99)"
If I run
SELECT * FROM View1;
or if I run
SELECT * FROM Table1 JOIN Table2 ON UPPER(Field1) LIKE UPPER(Field99);
from sqlite shell it returns the rows just fine.
But if i try to run those SQL statements in C++ with this code:
if(sqlite3_prepare_v2(database,SQLStr,-1,&stmt,0)==SQLITE_OK){
int cols = sqlite3_column_count(stmt);
printf("Query OK: %s \nColumns: %i\n", SQLStr, cols);
while(1) {
status = sqlite3_step(stmt);
if(status == SQLITE_ROW) {
printf("This is a row!\n");
} else if(status == SQLITE_DONE) {
printf("Rows handled!\n");
break;
} else {
printf("Other status!\n");
break;
}
}
}
It just returns:
Rows handled: SELECT * FROM View1;
Columns: 7
Rows handled!
But it doesn't return any rows like the shell does. (there should be "This is a row!" printed for every row in query. I tried to add table names to queries but no help. I also tried to run for example SELECT * FROM Table1; and then the C++ returns the rows. Is it so that SQLITE in C++ can't handle JOINS or Views?
if u want to collect the rows then use Sqlite3_exec() API or column API.sqlite3_step() only evaluates the prepared statement it does not fetch the results, for that u have to use different api available or u can use sqlite3_exec which is a wrapper.
check the following link
http://www.sqlite.org/c3ref/funclist.html
http://www.sqlite.org/cintro.html
All the best.