I am working on my own SNMP agent and am having trouble processing strings. I am pretty new to SNMP as well.
I've referred to the following links for some of implementing my own agent :
http://www.net-snmp.org/dev/agent/ucdDemoPublic_8c_source.html
http://www.net-snmp.org/dev/agent/example_8h_source.html
The second link shows exactly how to handle when a user attempts to set an integer type MIB object :
line 657 shows :
intval = *((long *) var_val);
My Question : How would I go about it with a string? I've tried casting it, strncpy, snprintf, etc.
My work :
I know, or at least think, that the following is legal :
int
setString(int action,
u_char * var_val,
u_char var_val_type,
size_t var_val_len,
u_char * statP, oid * name, size_t name_len)
{
unsigned char publicString[10];
static long intval;
char *cmd_string = NULL;
/*
* Define an arbitrary maximum permissible value
*/
switch (action) {
case RESERVE1:
//intval = *((long *) var_val);
/*
* Check that the value being set is acceptable
*/
if (var_val_type != ASN_OCTET_STR) {
DEBUGMSGTL(("setString", "%x not string type", var_val_type));
return SNMP_ERR_WRONGTYPE;
}
if (var_val_len > 1 ) {
DEBUGMSGTL(("setString", "wrong length %" NETSNMP_PRIz "u",
var_val_len));
return SNMP_ERR_WRONGLENGTH;
}
if ( !(var_val[0] == '1' || var_val[0] == '0') )
{
DEBUGMSGTL(("setString", "wrong value %s", var_val));
return SNMP_ERR_WRONGVALUE;
}
I know it is somewhat working because when I invoke
# snmpset -v 2c -c xxx 10.20.30.40 1.3.6.1.4.1.54321.3.0 s 3
Error in packet.
Reason: wrongValue (The set value is illegal or unsupported in some way)
Failed object: MY-TEST-MIB::testSnmp.3.0
AND
snmpset -v 2c -c xxx 10.20.30.40 1.3.6.1.4.1.54321.3.0 s 1
MY-TEST-MIB::testSnmp.3.0 = STRING: "1"
This proves to me at least the last collection of code is working.
Here is the action part :
case ACTION:
/*
* Set the variable as requested.
* Note that this may need to be reversed,
* so save any information needed to do this.
*/
if ( var_val[0] == '1' )
{
//do stuff - there realy is a script call here that does something
}
if ( var_val[0] == '0' )
{
//do stuff - there realy is a script call here that does something
}
break;
The above section I cannot get to work.
I've been able to get what I want by making the object an INTEGER (ASN.1) type but I can't do that because when reading this object it returns a STRING (ASN.1).
Related
I'm using "MariaDB Connector/C" for my homework, but I got a problem: I always get an empty string when I pass in a string parameter, the db table is:
MariaDB none#(none):test> SELECT * FROM t3
a
b
0
abc
1
bcd
2
af
3 rows in set
Time: 0.010s
MariaDB none#(none):test> DESC t3
Field
Type
Null
Key
Default
Extra
a
int(11)
NO
PRI
b
char(10)
YES
2 rows in set
Time: 0.011s
And the code I use to test:
#include <mysql/mysql.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
MYSQL *mysql;
mysql = mysql_init(NULL);
if (!mysql_real_connect(mysql,NULL , "none", "linux", "test", 0,"/tmp/mariadb.sock",0)){
printf( "Error connecting to database: %s",mysql_error(mysql));
} else
printf("Connected...\n");
if(mysql_real_query(mysql,"SET CHARACTER SET utf8",(unsigned int)sizeof("SET CHARACTER SET utf8"))){
printf("Failed to set Encode!\n");
}
char query_stmt_2[]="select * from t3 where b=?";
MYSQL_STMT *stmt2 = mysql_stmt_init(mysql);
if(mysql_stmt_prepare(stmt2, query_stmt_2, -1))
{
printf("STMT2 prepare failed.\n");
}
MYSQL_BIND instr_bind;
char instr[50]="abc";
my_bool in_is_null = 0;
my_bool in_error = 0;
instr_bind.buffer_type = MYSQL_TYPE_STRING;
instr_bind.buffer = &instr[0];
char in_ind = STMT_INDICATOR_NTS;
instr_bind.u.indicator = &in_ind;
unsigned long instr_len=sizeof(instr);
// instr_bind.length = &instr_len;
// instr_bind.buffer_length=instr_len;
instr_bind.is_null = &in_is_null;
instr_bind.error = &in_error;
MYSQL_BIND out_bind[2];
memset(out_bind, 0, sizeof(out_bind));
int out_int[2];
char outstr[50];
my_bool out_int_is_null[2]={0,0};
my_bool out_int_error[2]={0,0};
unsigned long out_int_length[2]={0,0};
out_bind[0].buffer = out_int+0;
out_bind[0].buffer_type = MYSQL_TYPE_LONG;
out_bind[0].is_null = out_int_is_null+0;
out_bind[0].error = out_int_error+0;
out_bind[0].length = out_int_length+0;
out_bind[1].buffer = outstr;
out_bind[1].buffer_type = MYSQL_TYPE_STRING;
out_bind[1].buffer_length = 50;
out_bind[1].is_null = out_int_is_null+1;
out_bind[1].error = out_int_error+1;
out_bind[1].length = out_int_length+1;
if(mysql_stmt_bind_param(stmt2, &instr_bind) ||
mysql_stmt_bind_result(stmt2, out_bind)){
printf("Bind error\n");
}
if(mysql_stmt_execute(stmt2))
{
printf("Exec error: %s",mysql_stmt_error(stmt2));
}
if(mysql_stmt_store_result(stmt2)){
printf("Store result error!\n");
printf("%s\n",mysql_stmt_error(stmt2));
}
while(!mysql_stmt_fetch(stmt2))
{
printf("%d\t%s\n", out_int[0], outstr);
}
mysql_stmt_close(stmt2);
end:
mysql_close(mysql);
}
I only got an empty result:
❯ ./Exec/test/stmt_test
Connected...
I have been in trouble with this for two days, and tomorrow is the deadline, I'm very anxious. Can you help? Thanks a lot!
1) General
Avoid "it was hard to write, so it should be hard to read" code
add variable declarations at the beginning of the function, not in the middle of code (Wdeclaration-after-statement)
don't use c++ comments in C
set character set with api function mysql_set_character_set()
write proper error handling, including mysql_error/mysql_stmt_error results and don't continue executing subsequent code after error.
always initialize MYSQL_BIND
2) input bind buffer
u.indicator is used for bulk operations and doesn't make sense here
bind.is_null is not required, since you specified a valid buffer address
buffer_length is not set (in comments)
3) Output bind buffer
Always bind output parameters after mysql_stmt_execute(), since mysql_stmt_prepare can't always determine the number of parameters, e.g. when calling a stored procedure: In this case mysql_stmt_bind_param will return an error.
binding an error indicator doesn't make much sense without setting MYSQL_REPORT_DATA_TRUNCATION (mysql_optionsv)
For some examples how to deal with prepared statements check the file ps.c of MariaDB Connector/C unit tests
I know that there probably was plenty on that but after several days of searching I am unable to find how to do one simple passing of integer and char in one go to PostgreSQL from C under Linux.
In PHP it is easy, like 123, and in C using libpq it seem to be like something out of ordinary.
I had a look at PQexecParams but is seem to be not helping. Examples on the net are not helping as well and it seems to be an impossible mission.
Would someone be kind enough to translate this simple PHP statement to C and show me how to pass multiple vars of different types in one INSERT query.
col1 is INT
col2 is CHAR
$int1 = 1;
$char1 = 'text';
$query = "INSERT INTO table (col1, col2) values ('$int1',$char1)";
$result = ibase_query($query);
This would show what I am trying to do (please mind the code is very wrong):
void insert_CommsDb(PGconn *conn, PGresult *pgres, int csrv0) { const char * params[1];
params[0] = csrv0;
pgres = PQexecParams(conn, "INSERT INTO comms_db (srv0::int) values ($1)",
1,
NULL,
params,
1,
NULL,
0);
if (PQresultStatus(pgres) != PGRES_COMMAND_OK)
{
fprintf(stderr, "INSERT failed: %s", PQerrorMessage(conn));
exit_nicely(conn,pgres);
}
PQclear(pgres);
}
https://www.postgresql.org/docs/current/static/libpq-exec.html
As #joop commented above:
If the paramTypes argument is NULL, all the params are assumed to be strings.
So, you should transform your int argument to a string.
void insert_CommsDb(PGconn *conn, int csrv0)
{
PGresult *pgres;
char * params[1];
char buff[12];
sprintf(buff, "%d", csrv0);
params[0] = buff;
pgres = PQexecParams(conn
, "INSERT INTO comms_db (srv0::int) values ($1)" // The query (we dont need the cast here)
, 1 // number of params
, NULL // array with types, or NULL
, params // array with parameter values
, NULL // ARRAY with parameter lenghts
, NULL // array with per-param flags indicating binary/non binary
, 0 // set to 1 if we want BINARY results, 0 for txt
);
if (PQrresultStatus(pgres) != PGRES_COMMAND_OK)
{
fprintf(stderr, "INSERT failed: %s", PQerrorMessage(conn));
exit_nicely(conn,pgres);
}
PQclear(pgres);
}
wildplasser's answer shows the way in general.
Since you explicitly asked about several parameters, I'll add an example for that.
If you are not happy to convert integers to strings, the alternative would be to use the external binary format of the data type in question. That requires inside knowledge and probably reading the PostgreSQL source. For some data types, it can also depend on the hardware.
PGresult *res;
PGconn *conn;
Oid types[2];
char * values[2];
int lengths[2], formats[2];
int arg0;
/* connect to the database */
/*
* The first argument is in binary format.
* Apart from having to use the "external binary
* format" for the data, we have to specify
* type and length.
*/
arg0 = htonl(42); /* external binary format: network byte order */
types[0] = 23; /* OID of "int4" */
values[0] = (char *) &arg0;
lengths[0] = sizeof(int);
formats[0] = 1;
/* second argument is in text format */
types[1] = 0;
values[1] = "something";
lengths[1] = 0;
formats[1] = 0;
res = PQexecParams(
conn,
"INSERT INTO mytab (col1, col2) values ($1, $2)",
2,
types,
(const char * const *)values,
lengths,
formats,
0 /* results in text format */
);
I'd recommend that you use the text format for most data types.
The notable exception is bytea, where it usually is an advantage to use the binary format, as it saves space and CPU power. In this case, the external binary format is simply the bytes.
VS C++ not liking htonl(42):
arg0 = htonl(42); /* external binary format: network byte order */
I have written and am testing software for a generic SNMP client module in C as well as an implementation using this generic module. I am having trouble getting a get request to work by passing in a MIB name(e.g. sysDescr) instead of an OID(e.g. 1.3.6.1.2.1.1.1).
I am successful when I pass in a character array containing the OID to _snmp_parse_oid()_ but not the name.
I have checked the MIB file to make sure I am using the correct name. When I run the command line SNMP translate on the name it gives me the OID listed above:
$ snmptranslate -m +<MIB File> -IR -On <MIB Name>
.#.#.#.#.#.#.#####.#.#.#.#.#.#
(In the above command I replaced my actual mib file with <MIB File>, mib name with <MIB Name>, and OID numbers returned from the command with # characters)
The following is my code for my generic SNMP get function, assume returned values are #define numbers and I have removed some error handling for brevity:
/// #Synopsis Function to send out get request since the
/// SNMPOidData object has been setup
///
/// #Param oid_name String containing the OID to set
/// #Param value Value to set
///
/// #Returns Error
int snmpGet(SNMPAgent *this, char const * const oid_name, SNMPOidData * value)
{
netsnmp_pdu *pdu;
netsnmp_pdu *response;
netsnmp_variable_list *vars;
oid *retrieved_oid;
oidStruct oid_to_get;
int status = 0;
int result = ERROR_SUCCESS;
// Create the PDU for the data for our request
pdu = snmp_pdu_create(SNMP_MSG_GET);
oid_to_get.OidLen = MAX_OID_LEN; // Set max length
// Send out the request(s)
retrieved_oid = snmp_parse_oid(oid_name, oid_to_get.Oid, &oid_to_get.OidLen);
// Set the data
snmp_add_null_var(pdu, oid_to_get.Oid, oid_to_get.OidLen))
// Send the request out
status = snmp_synch_response(this->port.snmp_session_handle, pdu, &response);
if (STAT_SUCCESS == status)
{
if (SNMP_ERR_NOERROR == response->errstat)
{
vars = response->variables;
value->type = vars->type;
if (vars->next_variable != NULL)
{
// There are more values, set return type to null
value->type = ASN_NULL;
}
else if (!(CHECK_END(vars->type))) // Exception
{
result = ERROR_NOT_PRESENT;
fprintf(stderr, "Warning: OID=%s gets snmp exception %d \n",
oid_name, vars->type);
}
else if ((vars->type == ASN_INTEGER)
|| (vars->type == ASN_COUNTER)
|| (vars->type == ASN_UNSIGNED))
{
value->integer = *(vars->val.integer);
value->str_len = sizeof(value->integer);
}
else
{
value->str_len = vars->val_len;
if (value->str_len >= MAX_ASN_STR_LEN)
value->str_len = MAX_ASN_STR_LEN;
if (value->str_len > 0)
memcpy(value->string, vars->val.string, value->str_len);
// guarantee NULL terminated string
value->string[value->str_len] = '\0';
}
}
}
this->freePDU(response); // Clean up: free the response
return result;
}
The error I am getting:
oid_name: Unknown Object Identifier (Sub-id not found: (top) -> <MIB Name>)
Which comes from the following call:
retrieved_oid = snmp_parse_oid(oid_name, oid_to_get.Oid, &oid_to_get.OidLen);
I have made sure that the MIB files are on the machine in the configured place (snmptranslate wouldn't work if this weren't the case).
I have spent a good amount of time on Google results as well as directly searching here on Stack Overflow.
The following is a good tutorial but does not address my issue (they directly reference the OID they want to get the value of):
http://www.net-snmp.org/wiki/index.php/TUT:Simple_Application
Any help or insight would be much appreciated.
Some other info I can think of is that this is being compiled to run on an armv5tejl target running Linux communicating with an external device via ethernet.
Thanks,
When I call MIB variables by their string name I use the following net-snmp functions.
read_objid(OID, anOID, &anOID_len);
snmp_add_null_var(pdu, anOID, anOID_len);
Where:
oid anOID[MAX_OID_LEN];
size_t anOID_len = MAX_OID_LEN;
In my program I pack this all into a single function call.
void packSingleGetOID(const char *OID, struct snmp_pdu *pdu){
// OID in / PDU out
oid anOID[MAX_OID_LEN];
size_t anOID_len = MAX_OID_LEN;
read_objid(OID, anOID, &anOID_len);
snmp_add_null_var(pdu, anOID, anOID_len);
}
I pass in the MIB OID string and the pointer to session pdu. Remember the OID string is MIB_Name::variable.
So I have the following project i need to work on for school. It involves a server/client communicating where the client sends requests to the server to get certain information. The server gets the req, parses it, and then sends a response based on the type of the request. For example:
GET /apple/fn/tom/ln/sawyer/a/25/id/1234 : This is a request to get the info for the following person who works at the Apple company (/apple):
fn (first name): Tom
ln (last name): Sawyer
a (age): 25
id (ID): 1234
Now the server should accept input, parse it, and return the information requested from its own database. implementing the server/client is not an issue. I need to know the best way to implement the algorithm to deal with input since not all requests would look like the one above. Other examples of requests:
GET /apple/i: This should return info about Apple company (i.e. address, phone number)
GET /apple/e: return number of employees in Apple company
GET /apple/e/id/1234: return info of employee in Apple company with the following id=1234 (which in our example would be Tom Sawyer) i.e. return first name, last name, age, address.
GET /apple/fn/tom/ln/sawyer/a/25/id/1234 : discussed above
SET /apple/fn/tom/ln/sawyer/a/25/id/5678 : update id of this employee to 5678
SET /apple/fn/tom/ln/sawyer/a/23 : update age of this employee to 23
...
I will have to implement different structs for req/response (i.e. a struct for req and another for response) as well as a different function for each of the different request/responses. BUT what is the best way to deal with parsing input && decide which function to send it to? I was told to look into using a parser-generator like Bison but to my understanding this would only help parse the input and break it up into pieces which is not that hard for me since I know I always have the "/" between fields so I can use the function:
strtok( input, "/" );
So main issue I have is how to decide where to send each request. So Assuming I have the following functions:
struct GetEmployeeInfoReq
{
char *fn;
char *ln;
int age;
};
struct GetEmployeeInfoResp
{
int house_num;
int street_num;
char *street_name;
char * postal_code;
int years_worked_here;
};
void GetEmployeeInfo( struct GetEmployeeInfoResp *resp, struct GetEmployeeInfoReq *req );
struct GetCompanyInfoReq
{
...
}
struct GetCompanyInfoResp
{
...
}
void GetCompanyInfo( struct GetCompanyInfoResp *resp, struct GetCompanyInfoReq *req );
Now I know that to call the first function I need the following request:
GET /apple/fn/tom/ln/sawyer/a/25/id/1234
and to call 2nd function I need the following:
GET /apple/i
My question is how to get this done? Off the top of my mind I'm thinking defining a variable for each possible field in the input req and using that so if this is my request:
GET /apple/e/id/1234
then I would have the following values defined and set to true:
bool is_apple = true;
bool is_employee = true;
bool is_id = true;
After I know that this request is:
GET /apple/e/id/<id_num>
AND NOT
GET /apple/e
so i can send it to the proper function.
Is this the correct approach as I'm lost on how to tackle this issue.
Thanks,
Get yourself a large piece of paper and make a diagram about the logic to get a grammar (not actually neede here, you could just parse it, but being a school assignment I assume that it is meant to be build up upon).
Some observations
every input starts with a /
every entry ends with a / except the last one
entries consist of characters and/or digits
empty entries gets you in trouble if you insist on using strtok (thanks to wildplasser, I missed that)
order matters?
Entries are
either key (one entry) or key/value (two consecutive entries).
In other words: the may or may not have an argument
Entries with a meaning
first entry is the company name (what do you do if that's all? Check assignment)
next entry is either
i print company info
e
without arguments: print #employees
with argument: print information about argument, argument must be correct
fn first name as a key, must have a value because of strtok, argument must be a string
ln last name as a key, must have a value because of strtok, argument must be a string
id id as a key, must have a value because of strtok, argument must be a string of digits (an integer so to say but it is still a string at that point) only
a age as a key, must have a value because of strtok, argument must be a string of digits (an integer so to say but it is still a string at that point) only
Examples:
/apple/e
/ start of input
apple company name. Must have an argument, so go on
e something with employers, my or may not have an argument so check.
EOI (end of input) that means that e has no arguments, so print the number of employees.
/apple/id/134
/ start of input
apple company name. Must have an argument, so go on
e something with employers, my or may not have an argument so check.
id is a key and must have a value, that means we need an argument, so check
1234 all digits which is correct value for id
EOI (end of input) no other things, so print the information you have about id-1234
/apple/fn/tom/ln/sawyer/a/25/id/1234
/ start of input
apple company name. Must have an argument, so go on
fn is a key, must have a value, check
tom the value for fn must be a string which it is, safe
ln is a key, must have a value, check
sawyer the value for ln must be a string which it is, safe
a is a key, must have a value, check
25 the value for ln must be a string of digits which it is, safe
id is a key, must have a value, check
1234 the value for ln must be a string of digits which it is, safe
EOI (end of input). The key for the database entry seems to be the name fn and ln, so read the entry tomsawyer and check if any of id or a is different and change accordingly. If nothing is different: check your assignment.
It might be good idea to build a struct with all of the informations and fill it while parsing (where I wrote "safe"). You need two main functions printIt(keys) and changeIt(key, value). Some things can be done immediately, like in my first example, some need further ado.
That's it, should be straightforward to implement.
EDIT a short example
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// ALL CHECKS OMMITTED!
#define DELIMITER '/'
// should resemble a row from the DB
typedef struct company {
char *comp_name;
unsigned int num_empl;
unsigned int empl_id;
unsigned int empl_age;
char *first_name;
char *last_name;
} company;
int main(int argc, char **argv)
{
company *entries;
char *input, *token;
size_t ilen;
if (argc < 2) {
fprintf(stderr, "Usage: %s stringtoparse \n", argv[0]);
exit(EXIT_FAILURE);
}
// work on copy
ilen = strlen(argv[1]);
input = malloc(ilen + 1);
strcpy(input, argv[1]);
entries = malloc(sizeof(company));
// skip first delimiter
if (*input == DELIMITER) {
input++;
ilen--;
}
token = strtok(input, "/");
if (token == NULL) {
fprintf(stderr, "Usage : %s stringtoparse \n", argv[0]);
exit(EXIT_FAILURE);
}
// first entry is the company name
entries->comp_name = malloc(strlen(token));
strcpy(entries->comp_name, token);
// mark empty entries as empty
entries->first_name = NULL;
entries->last_name = NULL;
entries->empl_age = -1;
entries->empl_id = -1;
// F(23)
entries->num_empl = 28657;
// only very small part of grammar implemented for simplicity
for (;;) {
token = strtok(NULL, "/");
if (token == NULL) {
break;
}
// "e" [ "/" "id" "/" number <<EOF>> ]
if (strcmp(token, "e") == 0) {
token = strtok(NULL, "/");
if (token == NULL) {
puts("Info about number of employees wanted\n");
// pure info, pull from DB (not impl.) and stop
break;
} else {
if (strcmp(token, "id") != 0) {
fprintf(stderr, "Only \"id\" allowed after \"e\" \n");
// free all heap memory here
exit(EXIT_FAILURE);
}
token = strtok(NULL, "/");
if (token == NULL) {
fprintf(stderr, "ERROR: \"id\" needs a number \n");
// free all heap memory here
exit(EXIT_FAILURE);
}
// does not check if it really is a number, use strtol() in prod.
entries->empl_id = atoi(token);
printf("Info about employee with id %d wanted\n", entries->empl_id);
// pure info, pull from DB (not impl.) and stop
break;
}
}
// "a" "/" number
else if (strcmp(token, "a") == 0) {
token = strtok(NULL, "/");
if (token == NULL) {
fprintf(stderr, "ERROR: \"a\" needs a number \n");
// free all heap memory here
exit(EXIT_FAILURE);
}
// does not check if it actually is a number, use strtol() in prod.
entries->empl_age = atoi(token);
printf("Age given: %d\n", entries->empl_age);
}
// "id" "/" number
else if (strcmp(token, "id") == 0) {
token = strtok(NULL, "/");
if (token == NULL) {
fprintf(stderr, "ERROR: \"id\" needs a number \n");
// free all heap memory here
exit(EXIT_FAILURE);
}
// does not check if it actually is a number, use strtol() in prod.
entries->empl_id = atoi(token);
printf("ID given: %d\n", entries->empl_id);
}
// "fn" "/" string
else if (strcmp(token, "fn") == 0) {
token = strtok(NULL, "/");
if (token == NULL) {
fprintf(stderr, "ERROR: \"fn\" needs a string \n");
// free all heap memory here
exit(EXIT_FAILURE);
}
entries->first_name = malloc(strlen(token));
strcpy(entries->first_name, token);
printf("first name given: %s\n", entries->first_name);
}
// "ln" "/" string
else if (strcmp(token, "ln") == 0) {
token = strtok(NULL, "/");
if (token == NULL) {
fprintf(stderr, "ERROR: \"ln\" needs a string \n");
// free all heap memory here
exit(EXIT_FAILURE);
}
entries->last_name = malloc(strlen(token));
strcpy(entries->last_name, token);
printf("last name given: %s\n", entries->last_name);
} else {
fprintf(stderr, "ERROR: Unknown token \"%s\" \n", token);
// free all heap memory here
exit(EXIT_FAILURE);
}
}
printf("\n\nEntries:\nCompany name: %s\nFirst name: %s\nLast name: %s\n",
entries->comp_name, entries->first_name, entries->last_name);
printf("Age: %d\nID: %d\nNumber of employees: %d\n",
entries->empl_age, entries->empl_id, entries->num_empl);
/*
* At this state you have information about what is given (in "entries")
* and what is wanted.
*
* Connect to the DB.
*
* If firstnamelastname is the DB-id and in the DB, you can check if
* the given ID is the same as the one in the DB and change if not.
*
* You can do the same for age.
*
* If firstnamelastname is not in the DB but ID is given check if the
* ID is in the DB, change firstname and/or lastname if necessary and
* congratulate on the wedding (many other reasons are possible, please
* check first or it might get really embarassing)
*/
// free all heap memory here
/* Disconnect from the DB */
exit(EXIT_SUCCESS);
}
Compiled with:
gcc -g3 -std=c11 -W -Wall -pedantic jjadams.c -o jjadams
try with
./jjadams "/Apple/fn/Tom/ln/Sawyer/a/10/id/3628800"
./jjadams "/Apple/e"
./jjadams "/Apple/e/id/1234"
./jjadams "/Apple/e/1234"
I'm having problems while fetching data from columns which have more than 255 characters
I got such an error message:
Open Client Message:
Message number: LAYER = (1) ORIGIN = (4) SEVERITY = (1) NUMBER = (132)
Message String: ct_fetch(): user api layer: internal common library error: The bind of result set item 3 resulted in truncation.
It fetches only the first 255 rows and truncates the rest.
I have tried to imply below lines before ct_connect but didn't work
CS_BOOL boolv = CS_TRUE;
CS_RETCODE retcode2 = ct_capability ( *connection, CS_GET, CS_CAP_REQUEST, CS_WIDETABLES, &boolv);
here are some part of the code , do you have any suggestion
for (i = 0; i < num_cols; i++) {
/*
** Get the column description. ct_describe() fills the
** datafmt parameter with a description of the column.
*/
retcode = ct_describe(cmd, (i + 1), &datafmt[i]);
if (retcode != CS_SUCCEED) {
ex_error("ex_fetch_data: ct_describe() failed");
break;
}
/*
** update the datafmt structure to indicate that we want the
** results in a null terminated character string.
**
** First, update datafmt.maxlength to contain the maximum
** possible length of the column. To do this, call
** ex_display_len() to determine the number of bytes needed
** for the character string representation, given the
** datatype described above. Add one for the null
** termination character.
*/
datafmt[i].maxlength = ex_display_dlen(&datafmt[i]) + 1;
/*
** Set datatype and format to tell bind we want things
** converted to null terminated strings
*/
datafmt[i].datatype = CS_LONGCHAR_TYPE;
datafmt[i].format = CS_FMT_NULLTERM;
/*
** Allocate memory for the column string
*/
coldata[i].value = (CS_CHAR *) malloc(datafmt[i].maxlength);
if (coldata[i].value == NULL) {
ex_error("ex_fetch_data: malloc() failed");
retcode = CS_MEM_ERROR;
break;
}
/*
** Now bind.
*/
retcode = ct_bind(cmd, (i + 1), &datafmt[i], coldata[i].value,
&coldata[i].valuelen, (CS_SMALLINT *) &coldata[i].indicator);
if (retcode != CS_SUCCEED) {
ex_error("ex_fetch_data: ct_bind() failed");
break;
}
}
.............
.............
.............
/*
** Fetch the rows. Loop while ct_fetch() returns CS_SUCCEED or
** CS_ROW_FAIL
*/
while (((retcode = ct_fetch(cmd, CS_UNUSED, CS_UNUSED, CS_UNUSED,
&rows_read)) == CS_SUCCEED) || (retcode == CS_ROW_FAIL)) {
Even we have faced problem when we are using Sybase with Uniface,but uniface sybase driver has got a option which will truncate the data and save in some other table but while fetching we have to fetch data from all the tables.
When using the native jconn*.jar drivers you have to set the "?CHARSET=iso_1" parameter on the JDBC driver to connect to a Sybase server with a default charset of Roman 8, otherwise you see this 255 characters truncation issue.
Could this be the problem you are experiencing?