‏הצגת רשומות עם תוויות c. הצג את כל הרשומות
‏הצגת רשומות עם תוויות c. הצג את כל הרשומות

יום חמישי, ינואר 18, 2024

זה שזה נמצא בספרייה או בדוגמת קוד לא אומר שזה טוב לשימוש תמיד בכל המצבים, כאשר fread אחד איטי יותר מfread אחר כמעט פי 2

לפני זמן מה עבדתי לפתור בעייה בספרייה מסויימת, במחשבים של המפתחים לא הייתה שום בעייה, אבל במחשבים אצל הלקוח בחלק מהזמן היו פונקציות שפתאום היה לוקח להם באזור השנייה לסיים לסיים בעוד במצב נורמאלי באותו המחשב זה היה לוקח כ 340 מילי שניות. לאחר חפירה די גדולה הגעתי למקטע קוד כזה:
 
     while (0 != (len = (fread(buffer, 1, sizeof(buffer), file))))
     {
         doSomething(buffer,sizeof(buffer));
     }
    
קוד דומה קיים ב openssl

     for (;;) {
         inlen = fread(inbuf, 1, 1024, in);
         if (inlen <= 0)
             break;
         if (!EVP_CipherUpdate(ctx, outbuf, &outlen, inbuf, inlen)) {
             /* Error */
             EVP_CIPHER_CTX_free(ctx);
             return 0;
         }
         fwrite(outbuf, 1, outlen, out);
     }
את זה אפשר לראות  בתיעוד של OpenSSL, עכשיו ל openssl לא ממש צריך להיות אכפת מהביצועים במקרה הקצה הזה, אבל במקומות אחרים זה יכול להיות מאוד חשוב.

בדקתי ובאמת הקוד הזה נלקח כמו שהוא מהתיעוד הרשמי של הספרייה בה השתמשו, רק שיש בו בעייה קטנטנה, בחלק מהפעמים הקריאות הללו לא ימרו ישירות ל read אלא ישונו באופטימיזציה ע"י הקומפיילר, אפילו יכול להיות מקרה בו הfread בפועל יומר למשהוא דמויי readahead. זכרתי שלפעמים freadים קטנים לוקחים פחות זמן מאשר freadים גדולים, אבל לא זכרתי על איזו חומרה זה קרה,  בהמון מקומות אנו יכולים לגלות שקריאות בצורה כזאת יהיו עדיפות , ייתכן וזה כי קל לנו יותר לבדוק שקראנו את כלל התוכן ואנחנו מרוויחים מזה שאנחנו קוראים תמיד בלוק קטן יותר, ואז אנחנו מקבלים את המידע מהר יותר מאשר במקרים בהם הfirmware צריך לבצע עוד מספר קריאות בשביל שניתן יהיה לספק לנו את כלל המידע שביקשנו. מפתחי הספרייה לקחו את מצב הביניים הטוב ביותר.

בשביל לבדוק את החשדות שלי בדקתי את שתי הגישות, ובאמת ראיתי שבמקרה של כוננים מכניים מרגישים את זה, באמת הייתה עדיפות לכתיבת של בלוקים גדולים כאשר. כאשר בדקתי עם רייד לא ראיתי את התופעה כלל.

מכיוון שזה היה מקרה מעניין בדקתי מצב דומה על המחשב האישי שלי וייצרתי את שני חלקי הקוד הבאים, ובדקתי תזמון בproduction וראיתי שזה באמת מוריד את המהירות
 
קריאה של בלוק גדול בפעם אחת
#include <stdio.h>
#include <stdlib.h>

int main()
{
    FILE * pt_file;
    char buff[1024];
    int i;
    pt_file= fopen("/tmp/binaryfile","rb");
    if (NULL == pt_file)
    {
       perror("Failed to read binaryfile");
       return 1;
    }
    for (i = 0 ;i < 1024*1024; i++)
    {
       if (1 != fread(buff,sizeof(buff),1 , pt_file))
       {
          fclose(pt_file);
          return 1;
       }
    }
    fclose (pt_file);
   return 0;
}

לקריאה של ביית אחת המון פעמים.

#include <stdio.h>
#include <stdlib.h>

int main()
{
    FILE * pt_file;
    char buff[1024];
    int i;
    pt_file= fopen("/tmp/binaryfile","rb");
    if (NULL == pt_file)
    {
       perror("Failed to read binaryfile");
       return 1;
    }
    for (i = 0 ;i < 1024*1024; i++)
    {
       if (1024 != fread(buff,1, sizeof(buff) , pt_file))
       {
          fclose(pt_file);
          return 1;
       }
    }
    fclose (pt_file);
   return 0;
}
ונבצע בדיקת ביצועיים ע"י hyperfine מקבלים על גבי nvme : 


Benchmark 1: ./out
 Time (mean ± σ):     103.6 ms ±   4.0 ms    [User: 23.6 ms, System: 80.0 ms]
 Range (minmax):   101.3 ms121.6 ms    24 runs

 
ו 
 
hyperfine ./out2
Benchmark 1: ./out2
 Time (mean ± σ):     104.1 ms ±   3.7 ms    [User: 23.5 ms, System: 80.5 ms]
 Range (minmax):   101.1 ms119.3 ms    24 runs

 
 
 

כן אנחנו רואים שאין שום הבדל אמיתי בבדיקה הזאת.

בין הפעלה של כל אחד מהם אני ייצרתי קובץ זבל ע"י dd if=/dev/urandom of=./binaryfile bs=1M count=1024

מכיוון שיש קריאות לcache אז hyperfine לא ייתן לנו זמן אמיתי לבדיקה של הקריאה ממערכת ההפעלה אז בדקתי גם על ידי 
#!/bin/bash
for i in $(seq 1 1 100) ;  
do  
  dd if=/dev/urandom of=./binaryfile bs=1M count=1024
  (time ./out)  >> out.txt 2>&1
done
וגם
#!/bin/bash
for i in $(seq 1 1 100) ;  
do  
  dd if=/dev/urandom of=./binaryfile bs=1M count=1024
  (time ./out2)  >> out2.txt 2>&1
done

סכמתי את התוצאות וחילקתי ב 100 הממוצע שלי היה 0.109 שניות מול 0.264 שניות, שזה די מעניין, מתברר שכונן המכני שלי תפקד די קרוב למה שה nvme עשה, אבל הפעם יצא שדווקא הקריאות הגדולות הציגו שקריאה בבלוקים גדולים עדיפה, עכשיו הבדיקה שלי הייתה של תהליך בודד אחד, כאשר במקרה שלי היה מדובר בעשרות התליכים כשהעומס המצטבר יכול בהחלט לגרום לעלייה פי שלוש בביצועים.

עכשיו יבוא אדם ויגיד למה אתה לא משתמש בfeof בסוף הדוגמה, אני אגיד שמכיוון שאני יודע ש feof משקר במקרה מסויים, וצריך באמת להשוות גודל הקובץ לכמות המידע שצריכה להקרא בחלק ממערכות ההפעלה, כי זו הדרך היחידה לדעת שקיבלנו מה שהיינו צריכים לקבל, אז למה לא הכנסתי את זה לקוד ? התעצלתי.

במקרה שלי, כנראה בגלל שמדובר בכוננים מכניים קריאות הfread בצורה הזאת גרמו לבעייה מתפתחת, שהכוננים וכלל המחשב ביחד התחילו לקבבל עומס גבוהה מדיי וגרם לקסקדת בעיות.

כולי שמח ומאושר, אצתי רצתי לחבילה שבה היה את הקוד הזה, רק בשביל לגלות שמתכנת אחר כבר תיקן את הקוד הזה לפני ! (במספר חודשים) עכשיו למה אני שידרגתי לפני כן ? שאלה מצויינת.

עכשיו, האם דרך אחת עדיפה על אחרת תמיד ? ממש לא , צריך לבצע בדיקת התאמה לציוד בכל פעם, זה למה יש לנו בדיקות חוזרות כל פעם.

יום שלישי, מאי 30, 2023

כשאומרים שיש משהוא שהוא בטוח, לפעמים זה לא אומר מה שאתה חושב שזה אומר

בתקופה האחרונה התחילו לעבור לשימוש במערכות בטוחות בשביל לההחליף את הדברים המקובלים שקיימים כבר שנים, משום מה חושבים שכשמדברים על דברים בטוחים לא יהיו חריגות. אם יצא לכם לכתוב בשפת C ב VS והשתמשתם בפונקציות strcpy או strcat יכול להיות שראיתם את ההמלצה החמה לעבור לשימוש בפונקציותה המסתיימות ב_s מכיוון שזה משהוא "בטוח".
 
הבעיה שהרבה מאוד אנשים לא מבינים מה אומרת המילה בטוח, ופשוט מאמצים את הבקשה ומחליפים את הקוד להשתמש ב _s ושוכחים מזה. הבעיה היא שדבר ראשון זה ממש לא קוד פורטבילי אלא שיש תופעת לוואי חריגה (ידועה) שהיא העלאת אירוע חריגה.
 
לדוגמה הקוד הלא תקין הבא:

#include <stdio.h>
#include <string.h>

void bad_function(const char * const argument_to_copy)
{
   char destination[10];
   strcpy(destination,argument_to_copy);
 }
int main()
{
  bad_function("this is an example of bad coding");
  return 0;
}

הקוד הזה לא נותן לנו שום התראה כברירת מחדל ב gcc ,אבל אם נקמפל זאת ב VS ונפעיל התראות נקבל המלצה להשתמש ב strcpy_s כי הוא בטוח יותר, אבל הבטיחות שלו מגיעה מזה שהחליפו את הoverflow הישן והטוב במשהוא גרוע שהוא זריקת חריגה שנושכת אותך בתוחס לאחר חצי שנה (כי משהוא שכח לבדוק את גודל המערך). 

למזלנו gcc לא מכיל את ההמלצה להשתמש בזה, ולנו בקבצי man יש הסבר טוב מאוד לגבי זה שאומר 

Bugs

If the destination string of a strcpy() is not large enough, then anything might happen. Overflowing fixed-length string buffers is a favorite cracker technique for taking complete control of the machine. Any time a program reads or copies data into a buffer, the program first needs to check that there's enough space. This may be unnecessary if you can show that overflow is impossible, but be careful: programs can get changed over time, in ways that may make the impossible possible.

מהניסיון שלי, המנעו משימוש בפונקציות _s, אבל בדקו את הגודל תמיד והשתמשו ב strncpy מלתחילה, השתמשו בקוד פורטבילי ולא קוד שיעבוד רק ב VS. חבל שהאנשים שעומדים מאחורי Visual Studio שוב פעם מנסים להפוך שפה פשוטה, ומקובלת למשהוא שיהיה רק של הקומפיילר הזה.

יום שני, דצמבר 06, 2021

אל תסמכו על berilios / github או sourceforge ועשו fork עצמאי

במשך שנים תמיד אמרתי לכולם לא למזלג ותמיד לעבוד עם ה upstream, אבל הפעם אני חייב לאכול את הכובע שלי , ולהודות בטעותי האסטרונומית.

הסיפור הוא בספריית GPL בשם OpenBlox, הספרייה הייתה קיימת בשנת 2008 ב sourceforge , עם השנים הפרוייקט נסגר וככל הנראה נמכר לחברה אחרת (F5), הפרוייקט המקורי והפתוח נסגר והוחלף בפרוייקט בשם אחר. 

במונחי אינטרנט מדובר כמעט בארכיולוגיה, במציאות אני לא מצליח למצוא את הקוד המקורי. 
קבצי קוד המקור הארוזים בzip יפה אינם , waybackmachine אינו מכיל את המידע שאני צריך (יש את דף הפרוייקט אבל לא את קוד המקור לצערי). 
 
הצלחתי בדרך לא דרך להשיג כמות גדולה של קבצי מקור (אבל רק קבצי header) של הפרוייקט אבל עדיין חסר לי את הרכיב שמייצר את tgl.lib (אם למישהוא יש את גרסת ה GPL באזור גירסה 2.7 יויכול לשתף אני אודה לו מאוד).

למדו מהכישלון שלי , וודאו שיש לכם מזלג/גיבוי של הפרוייקט שאתם צריכים.

יום שישי, אפריל 22, 2016

ביצוע שאילתה מ PG ב C

איך לקבל נתונים מ PG באמצעות שימוש ב libpq:
#include <stdio.h>
#include <string.h>
#include <postgresql/libpq-fe.h>

void usage();

int main(int argc , char * argv[])
{
  PGconn          *db_connection;
  PGresult        *connection_result;
  const  char *    connection_info;
  PGresult        *request_result;
  int              number_of_records;
  int              number_of_fields;
  int              column_index;//column index in record 
  int              record_index;//record index under process

  db_connection  = NULL;
  connection_result = NULL;
  request_result = NULL;

  if (argc != 2) 
  {
    usage (argv[0]);
    return 1;
  }
  connection_info = argv[1];
  db_connection = PQconnectdb(connection_info);
  if (NULL == db_connection)
  {
    printf("%s:%d Failed connection to %s\n",
            __FUNCTION__,__LINE__, connection_info);
    return 1;
  }

  if (CONNECTION_BAD == PQstatus(db_connection) )
  {
     printf("%s:%d bad connection to %s=>%s\n",
             __FUNCTION__,__LINE__, 
             connection_info,
             PQerrorMessage(db_connection));

     goto error;
  }
  //execute over a db connection, the executation may fail or success
  request_result =  PQexec(
                           db_connection,
                           "SELECT field1 FROM mytable where field2=value");
  if (NULL == request_result)
  {
    printf("%s:%d Failed to execute command with error: %s\n",
           __FUNCTION__,__LINE__, PQerrorMessage(db_connection));
    goto error;
  }
  //checing here for PGRES_TUPLES_OK and not PGRES_COMMAND_OK because the request need to return data
  if (PQresultStatus(request_result) != PGRES_TUPLES_OK)
  {
     printf("%s:%d Failed to execute command with error : %s\n",__FUNCTION__,__LINE__, PQerrorMessage(db_connection));
     goto error;
  }
  number_of_records  = PQntuples(request_result);
  number_of_fields   = PQnfields(request_result);

  if (0 >   number_of_fields)
  {
     printf("%s:%d Invalid number of fields in result set (%d) : %s\n",__FUNCTION__,__LINE__, 
     number_of_fields,
     PQerrorMessage(db_connection));

     goto error;
  }
 
  printf("Result set have %d records and %d fields each\n,number_of_records,number_of_fields);
 
  for (record_index = 0 ; record_index < number_of_records;record_index++)
  {
    printf("record %d/%d:\n",record_index,number_of_records);
    for (column_index = 0 ; column_index < number_of_fields;column_index++)
    {
      printf("%d/%d field value <%s>\n",PQgetvalue(request_result, record_index, column_index));
    }
    printf("\n");
  }
  PQfinish (db_connection);
  return 0;

error:
  if (NULL != request_result)
  {
    PQclear(request_result);
  }
  if (NULL != connection_result)
  {
    PQclear(connection_result);
  }
  if (NULL!= db_connection) 
  {
    PQfinish (db_connection);
  }
  return 1;

}

void usage(const char * program_name)
{
  printf("USAGE: %s connection_string\n"
         "connection_string := keyValue|URI\n",
         "URI example:\n"
         "postgresql://other@localhost/otherdb?connect_timeout=10&application_name=myapp&client_encoding=UTF8\n",
         program_name);
}

יום חמישי, אוגוסט 20, 2015

בדיקות דרך קומפילציה

לא מזמן יצא לי לדבר על בדיקות עם ידידי משכבר הימים ויצא לי להציג שיטות תאומות שאני אוהב לדחוף -

היום הרבה מידידי משתמשים בכלים מצויינים כמו ג'נקינס ו TFS (יש הרבה אחרים אבל הרעיון דומה), מאוד בגדול מדובר על מערכות שדוגמת גרסה מסויימת מריצה מספר בדיקות ונותנת את תוצאת הבדיקות (אצלי המצב טיפה שונה כי יש פתרון in-house) ומדווחת למתכנתים .

השיטה הקלאסית שאני מכיר מדברת על מודל בדיקות שמופעל חיצונית למודל הפנימי שלך או מופעלים בזמן ריצה (מקרה קלאסי של gtest):


#include <iostream>
#include <gtest/gtest.h>

struct foo
{
  int random(){return 4;};//chosen by fair dice roll.
                          //guarnteed to be random :P
                          //by xkcd 221

};



TEST(foo, random) {
  foo f;
  EXPECT_EQ(0, f.random());
}



int main (int argc, char **argv)

{
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

שיטה נוספת מדברת על בנייה ספציפית לקומפיילר בה מקפלים מקרה בודד לבד כל פעם (מה שעושים עם lit ו QTest הרבה מאוד פעמים) , אמנם זה פתרון טוב הרבה מאוד פעמים זה לא עונה על הצרכים שלי (לא להחזיק מקטעי קוד נפרדים).

הבעייה בגישה של בדיקה בזמן ריצה (אפילו בתחילת ריצה) או בזמן בנייה כטסריט חיצוני  היא שזה עדיין משאיר פתח עצום למתכנת לעשות קומיט לדחוף אותו לרפוסטורי כשהוא לא נבנה (אני באמת רוצה לראות את איש ה IT שיצליח לדחוף הוק שמבצע בנייה על מכונה של מפתח שתמנע ממנו לעשות push אם בנייה נכשלת).

עכשיו בגלל שאני חי בעולם יחסית טוב (בכל זאת כותב בשפות מקומפלת) יש לי אפשרות נוספת שהתרגלתי להשתמש בה - כתיבת בדיקות שמכשילות קומפילציה שזה בשונה מבניית פרוייקט כן מתקבלת כקרס (גם ב git ו גם svn) בצד מפתח.

אני יודע זה נשמע עקום מאוד בהתחלה אבל ברגע שמתרגלים לזה זה נהיה מאוד נוח, דבר ראשון זה לא דורש שום סיפריה חיצונית (וחברי שנתקלו בבירוקרטיה לינקוקית יודעים על מה אני מדבר) ודבר שני זה מרגיש מאוד טבעי בקוד.

דוגמה קלאסית למטה דטה של מבנים (שתכשיל קומפילציה ) :



#include <iostream>
#include <cstddef>
#include <type_traits>

struct mytype_t
{
   //these are just example for some fields 
   int field1;
   int field2;

//   int non_declared_field_in_cmo_field_table_name;//this field had not been declared in cmo_field_table_name

   char y[3];
};

struct metadata_t
{
   size_t size;       
   size_t offset;     
   size_t allignment; 
   const char *  name;

} ;

//we have macro that fill metadata_t
//

static constexpr const struct metadata_t cmo_field_table_name[] = 
{
  {sizeof(mytype_t::field1),offsetof(mytype_t,field1) , std::alignment_of < decltype (mytype_t::field1 )> :: value,"1"},
  {sizeof(mytype_t::field2),offsetof(mytype_t,field2) , std::alignment_of < decltype (mytype_t::field2 )> :: value,"bla"},
  {sizeof(mytype_t::y),offsetof(mytype_t,y), std::alignment_of <decltype( mytype_t::y) > :: value,"2"},
};



//We declare a type that will fail compilation in case
template <bool>

struct check_struct_for_missing_field
{

};

template <>

struct check_struct_for_missing_field<true>
{
 //I named this field as field and not anything else so the compiler error will be more understandble 
 enum {field = 0};
};


//the magic start here 
//a recursive defintion that check on compile time if all the previous fields had been declared correctly
//in case of non declared fields this will fail compilation and will give us an error that will prevent a person commiting errosly like this mornign
//
//compile_type_check.cc: In instantiation of ‘struct check_if_all_fields_for_struct_correctly_declared_argument_is_field_number<((const metadata_t*)(& cmo_field_table_name)), 2>’:
//compile_type_check.cc:74:178:   required from here
//compile_type_check.cc:62:9: error: ‘field’ is not a member of ‘check_struct_for_missing_field<false>’
//    enum {check =  check_struct_for_missing_field < check_if_all_fields_for_struct_correctly_declared_argument_is_field_number < ptr,index_in_ptr - 1> :: value == ptr[index_in_ptr].offset  > ::field};
//             ^
//             compile_type_check.cc:74:1: error: static assertion failed: missing fields in tab
//              static_assert(check_if_all_fields_for_struct_correctly_declared_argument_is_field_number <cmo_field_table_name, sizeof(cmo_field_table_name)/sizeof(cmo_field_table_name[0]) -1 >::value == sizeof(mytype_t) , "missing fields in tab");
//
//
template < const struct metadata_t * ptr, int index_in_ptr>
struct check_if_all_fields_for_struct_correctly_declared_argument_is_field_number
{
  //incorrect declartion (type mismatchs) are found here
  //a correct offset of field N is  (alligment + size + offset ) for field N - 1
  enum {value =
        (
          ( ptr[index_in_ptr].allignment != ptr[index_in_ptr].size)?

            ptr[index_in_ptr].size + ptr[index_in_ptr].allignment
            :
            ptr[index_in_ptr].size)
            //this will iterate for the previous fields until the first declared in ptr
          + check_if_all_fields_for_struct_correctly_declared_argument_is_field_number<ptr,index_in_ptr-1>::value};
   //in case we have a missing field the next will fail compilation saying : 
   //
   enum {check =  check_struct_for_missing_field < check_if_all_fields_for_struct_correctly_declared_argument_is_field_number < ptr,index_in_ptr - 1> :: value == ptr[index_in_ptr].offset  > ::field};



};

//this is the final step of fields (the zero in the index

template <const struct metadata_t * ptr>

struct check_if_all_fields_for_struct_correctly_declared_argument_is_field_number <ptr,0>
{
 public:
   enum {value = //cmo_field_table_name[0].offset + 
        ( ptr[0].allignment != ptr[0].size)? ptr[0].size +   ptr[0].allignment :ptr[0].size
       };
};


static_assert(check_if_all_fields_for_struct_correctly_declared_argument_is_field_number <cmo_field_table_name, sizeof(cmo_field_table_name)/sizeof(cmo_field_table_name[0]) -1 >::value == sizeof(mytype_t) , "missing fields in tab");



int main(int argc, char *argv[])
{
  for (auto & r: cmo_field_table_name)
  {

    std::cout << "name:" << r.name<< " offset:" << r.offset << " allignment: " << r.allignment << " size:" << r.size << "total: " << r.size + r.allignment << std::endl;

  };

  return 0;

};

בחרתי ב C++  כי לדעתי היא פשוטה מספיק להבנה אבל דבר מאוד דומה ניתן לבצע גם בשפות אחרות.

JAVA: כן אני זוכר את הנושא של המרה לobject ובעיית הstatic הנקודה היא שימוש בטיפוסי הנתונים כמשתנים.

C: יחי המקרו ! יאפ בנייה על בסיס מקרו.

FPC: אם משהו עדיין משתמש בזה - "

FPC 2.4.0 is neither complete nor practically usable yet. Until that is so, templates can be used." אבל אני די בטוח שיש פתרון אחר.

הכשלת קימפול בגלל בדיקות מאפשרת יכולת נבוט טובות יותר ואני מאמין עם ערך wtf/minute נמוך יחסית.

יום שני, מרץ 25, 2013

כישלון בטעינת מחיצות דרך סמבה

שמתי לב שבדביאן SID טעינה אוטומטית של תיקיות משותפות אינה עובדה יותר עם ההגדרות ישנות.
לאחר מספר ימים של חיפוש בתיעוד עברתי על הקוד של mount.cifs.c וגיליתי לתדהמתי כי הפורמט השתנה ל :
user=username
pass=password

(הפעולה התקבלה למרות מה שרשום בתיעוד).

טיול לערוץ הסמבה בעלה החופשי גילה לי כי החבילה האחראית על פעולות העיגון הופרדה מה שמייצר מצב תאורטי שבעת שידרוג cifs.ko (כן בעת שידרוג קרנל) יגרום למערכת בשגיאות מוזרות בעת עגינה כגון כישלון מחיצה לקריאה בלבד כאשר יש סיסמה לא נכונה.

עידכון של cifs-utils ל 5.5 עם קרנל 3.2 בדביאן פתר את הבעיה, אולם לאחר מכן הלכתי להמשיך לטייל בקוד וגיליתי לתומי כי שני מבני ההגדרה שעובדים מה שהטריד אותי (למה התיעוד אומר א' כאשר בפועל קורה ב' (ב' ⊂ א') ).

ואז לגמרי במקרה אני רואה :
 
       /* tell the caller which value target points to */
        if (strncasecmp("user", line, 4) == 0)
                return CRED_USER;
        if (strncasecmp("pass", line, 4) == 0)
                return CRED_PASS;
        if (strncasecmp("dom", line, 3) == 0)
                return CRED_DOM;

שזה כנראה הולכת להפוך לדוגמה של מה לא לעשות בבקשה אצלנו, התרגלתי לכתוב שבניגוד לחלק מהמקומות דואג שכל ילד יוכל להבין מה הולך לראות ואיפה יש באג פוטנציאלי (חוסר התאמה) עם תיעוד.

וזה ילדים מה קורה כשהייתי כולה ה צריך לעשות להבין pam_mount הפסיק לעבוד אצלו.

יום שני, מרץ 11, 2013

טיפוס מוררכב שיוצר ב wsdl2h לא מכיל namespace

לפני מספר ימים פנה אליי עמית לעבודה עם תופעה מוזרה בקבצים שיוצרו באמצעות gSOAP.

הבעיה הייתה שטיפוסים מורכבים (ComplexType) בהודעות לא הכילו את מרחב השמות (namespace) ברירת המחדל של הרכיב.

הפעולה התבצעה ע"י wsdl2h -c filename.wsdl

במקום לקבל :

<SOAP-ENV:Body>
  <ns1:typename>
    <fieldname>
      <ns2:typename1>text value</ns2:typename1>
      <ns2:typename2>true</ns2:typename2>
      <ns2:typename3>6308880000</ns2:typename3>
    </fieldname>
  </ns1:typename>
</SOAP-ENV:Body>

התקבל:


<SOAP-ENV:Body>
  <ns1:typename>
    <ns1:fieldname>
      <ns2:typename1>text value</ns2:typename1>
      <ns2:typename2>true</ns2:typename2>
      <ns2:typename3>6308880000</ns2:typename3>
    </ns1:fieldname>
  </ns1:typename>
</SOAP-ENV:Body>

הפתרון לבעיה פשוט - יש להכריז כי מרחב השמות ברירת המחדל יהיה qualified (תמיד יכיל את מרחב השמות).
פעולה זה מתבצעת ע"י שינוי בקובץ ההכרזה :

#define SOAP_NAMESPACE_OF_ns1   "http://myrtfm.blogspot.com/soap/ServiceName"
//gsoap ns1   schema namespace: http://myrtfm.blogspot.com/soap/ServiceName
//gsoap ns1   schema form:      unqualified
ל:
#define SOAP_NAMESPACE_OF_ns1   "http://myrtfm.blogspot.com/soap/ServiceName"
//gsoap ns1   schema namespace: http://myrtfm.blogspot.com/soap/ServiceName
//gsoap ns1   schema form:      qualified



נ.ב. קובץ הסכמה הגדיר את מרחב שמות המרחב כראוי :

 elementFormDefault="qualified"

יום שישי, ינואר 06, 2012

איך לשלוף נתונים מקבצי mdb בדביאן

ב 2003-5 עזרתי לפתח מערכת של ניהול מלאי. מכיוון שלא היה לי אפשרות להשתמש ב קומפיילר ומה שהיה ברשותי היה רק כלי office אספנו ופיתחנו מערכת שידעה בהתאם לתיאור של הכלי לתת את הקישור למסח"א , מק"ט מספר יצרן או שם פריט.
לפני שהעברנו את הקוד פנימה יצרנו מערכת דמה שעבדה על Win XP. לא מזמן פגשתי את השותף לפיתוח וחשבתי לראות בעיניים עם ניסיון על פתרון שנכתב אי שם לפני שנים.

1. האם אני יכול לגשת למידע.
2. האם אני להשתמש בטפסים ובקוד שקיים (VBS ).

המידע מאוחסן ב mdb שנוצר ע"י אקסס ואני משתמש במערכת דביאן / גנו לינוקס, אז אמרתי איך אני שולף את המידע ?

שברתי את הראש איך לשלוף את המידע ואז גילתי את mdbtools :
apt-cache search mdbtools
libreoffice-base - office productivity suite -- database
libmdbtools - mdbtools libraries
mdbtools - JET / MS Access database (MDB) tools
mdbtools-dev - mdbtools development files
mdbtools-gmdb - JET / MS Access database (MDB) file viewer
באמצעות הכלי הנראה mdbtools ניתן לגשת למידע שמאוחסן בקבצי MDB (יש לי קבצים שנוצרו ב access 2000 ) :

Gui :

mdbtools-gmdb - כמה שזה עצוב הממשק מזכיר מאוד את Access 97 (אני מרגיש דינוזאור), אבל לאנשים שזוכרים את אקסס ושומרים לו חסד נעורים יבינו איך לעבוד.
הוא הצליח  המידע הגולמי היה זמין עבור northwind :
 CLI:

mdbtool-tables - מציג את הטבלאות.
mdb-tables Nwind.mdb

Order Details Orders Products Shippers Categories Customers Employees Suppliers

mdbtool-schema - מציג את הסכמה.



mdb-schema Nwind.mdb

-------------------------------------------------------------                                                                                                                      
-- MDB Tools - A library for reading MS Access database files                                                                                                                      
-- Copyright (C) 2000-2004 Brian Bruns                                                                                                                                             
-- Files in libmdb are licensed under LGPL and the utilities under                                                                                                                 
-- the GPL, see COPYING.LIB and COPYING files respectively.                                                                                                                        
-- Check out http://mdbtools.sourceforge.net                                                                                                                                       
-------------------------------------------------------------                                                                                                                      
                                                                                                                                                                                   
DROP TABLE Order Details;                                                                                                                                                          
CREATE TABLE Order Details                                                                                                                                                         
 (                                                                                                                                                                                 
        OrderID                 Long Integer,                                                                                                                                      
        ProductID                       Long Integer,                                                                                                                              
        UnitPrice                       Currency,                                                                                                                                  
        Quantity                        Integer,                                                                                                                                   
        Discount                        Single                                                                                                                                     
);                                                                                                                                                                                 
-- CREATE ANY INDEXES ...                                                                                                                                                          
                                                                                                                                                                                   
DROP TABLE Orders;                                                                                                                                                                 
CREATE TABLE Orders                                                                                                                                                                
 (                                                                                                                                                                                 
        OrderID                 Long Integer,                                                                                                                                      
        CustomerID                      Text (5),                                                                                                                                  
        EmployeeID                      Long Integer,                                                                                                                              
        OrderDate                       DateTime (Short),                                                                                                                          
        RequiredDate                    DateTime (Short),                                                                                                                          
        ShippedDate                     DateTime (Short),
        ShipVia                 Long Integer,
        Freight                 Currency,
        ShipName                        Text (40),
        ShipAddress                     Text (60),
        ShipCity                        Text (15),
        ShipRegion                      Text (15),
        ShipPostalCode                  Text (10),
        ShipCountry                     Text (15)
);
-- CREATE ANY INDEXES ...

DROP TABLE Products;
CREATE TABLE Products
 (
        ProductID                       Long Integer,
        ProductName                     Text (40),
        SupplierID                      Long Integer,
        CategoryID                      Long Integer,
        QuantityPerUnit                 Text (20),
        UnitPrice                       Currency,
        UnitsInStock                    Integer,
        UnitsOnOrder                    Integer,
        ReorderLevel                    Integer,
        Discontinued                    Boolean
);
-- CREATE ANY INDEXES ...

DROP TABLE Shippers;
CREATE TABLE Shippers
 (
        ShipperID                       Long Integer,
        CompanyName                     Text (40),
        Phone                   Text (24)
);
-- CREATE ANY INDEXES ...

DROP TABLE Categories;
CREATE TABLE Categories
 (
        CategoryID                      Long Integer,
        CategoryName                    Text (15),
        Description                     Memo/Hyperlink (255),
        Picture                 OLE (255)
);
-- CREATE ANY INDEXES ...

DROP TABLE Customers;
CREATE TABLE Customers
 (
        CustomerID                      Text (5),
        CompanyName                     Text (40),
        ContactName                     Text (30),
        ContactTitle                    Text (30),
        Address                 Text (60),
        City                    Text (15),
        Region                  Text (15),
        PostalCode                      Text (10),
        Country                 Text (15),
        Phone                   Text (24),
        Fax                     Text (24)
);
-- CREATE ANY INDEXES ...

DROP TABLE Employees;
CREATE TABLE Employees
 (
        EmployeeID                      Long Integer,
        LastName                        Text (20),
        FirstName                       Text (10),
        Title                   Text (30),
        TitleOfCourtesy                 Text (25),
        BirthDate                       DateTime (Short),
        HireDate                        DateTime (Short),
        Address                 Text (60),
        City                    Text (15),
        Region                  Text (15),
        PostalCode                      Text (10),
        Country                 Text (15),
        HomePhone                       Text (24),
        Extension                       Text (4),
        Photo                   OLE (255),
        Notes                   Memo/Hyperlink (255),
        ReportsTo                       Long Integer
);
-- CREATE ANY INDEXES ...

DROP TABLE Suppliers;
CREATE TABLE Suppliers
 (
        SupplierID                      Long Integer,
        CompanyName                     Text (40),
        ContactName                     Text (30),
        ContactTitle                    Text (30),
        Address                 Text (60),
        City                    Text (15),
        Region                  Text (15),
        PostalCode                      Text (10),
        Country                 Text (15),
        Phone                   Text (24),
        Fax                     Text (24),
        HomePage                        Memo/Hyperlink (255)
);
-- CREATE ANY INDEXES ...



-- CREATE ANY Relationships ...

-- relationships are not supported for access


ממשק תכנות באמצעות mdbtools-dev בשפת C

טוב לאחר שיש לי גישה למידע האם אפשר להריץ את הקוד ולעבוד ?
כנראה שאני מפספס משהוא אבל אני ממש לא מצליח להפעיל את ה formים שנכתבו ונערכו , בעסה אבל לא נורא.

אולי זה הזדמנות מצויינת לכתוב ממשק ב QT לעבודה מול זה.

יום רביעי, דצמבר 21, 2011

זיהוי ששדה מסויים לא הוגדר ב gsoap

לא מזמן התמודדתי עם צורך בזיהו אלמטים שלא הוכנסו ב gSOAP בספריה ורציתי לשתף.

ב gSOAP כאשר את מכריזים על חבר ב struct ברירת המחדל שלו היא optional ו nillable, זה אולי לא נשמע בעייה אבל זה נותן תחושה ממש לא טובה.

כאשר יש לכם שדה שהוא optional קשה לגלות עם השדה הוכנס או לא בהודעה (כלומר אין משהוא ברירת מחדל שיתן לכם את זה).
כל יחדת מידע שאנו עובדים איתה מאוכסנת באיזה שהוא struct (שקשור לWS איתו אנו עובדים).
לדוגמה :

typedef struct {
      char * field1;
      char * field2; 
      .
      .
      .
      time_t fileK;
      unsigned int fieldZ; 
} foo_t;

אנו נעבד את מבנה הנתונים בכניסה לפונקציה  - נעבור שדה שדה ונבדוק את תוכנו.
כלומר עבור פונקציה גנרית שתעבד כל WS אנו נעבור על שדה שדה.
לכן יש לנו offset גודל וטיפוס בשביל לעבד את המידע.

על מנת להתמודד עם התופעה:

עבור char * :  עם שדה לא הוגדר אז : 
NULL == *(char ** field_offset)
אם התנאי הזה מתקיים אז השדה שאתם עובדים עליו לא היה בהודעה.
כלומר missing element in xml

אם לעומת זאת הערך הוא ריק אז מתקיים:
'\0' == *((char ** )field_offset)
כלומר יש לכם : <node></node>

עבור ערך בסיסי תקבלו (חשוב לזכור ש field_offset הוא void *).
 
NULL == ((int *) field_offset)
NULL == ((double *) field_offset)

שימו לב שבמעבר על כל אחד מהשדות יש להזהר ולהשתמש בגודל האמיתי.

יום רביעי, דצמבר 07, 2011

להבין שאתה באמת צריך זה עוד מימוש של סיפריה

 זוכרים את הwsdl של אחת ממערכות ה CRM שנלחמתי בה לאחרונה ?
נו זאת אם ה WSDL שלא מתאים לצד השרת.

מאוד התלהבתי מהעובדה שעכשיו זה עובד והגעתי לנקודה שהבנתי שאני יכול לממש pim plugin שיעבוד עם thunderbird + lightining + provider/ kontact

התחלתי לממש את ה library שאוכל להכניס ואז ירד לי האסימון - אני ממציא מחדש את libgcal רק עבור CRM אחר. כמה שעות של טיול בקוד ואני מבין ש:
  • צריך ספרייה שתוכל לתת מידע מהקישור בין המכונה המריצה לבין מערכת ה CRM.
  • צריך להשתמש בספריות כמה שיותר גנריות וכמה שפחות תפורות למערכת (היה שלום MSXML).
  • צריך קוד ב C טהור - צריך להבין איך לעשות נכון את ניהול ועיבוד המידע  (pthread לא בא בחשבון)
  • צריך פתרון פשוט בשביל לא לגמור עם עוד רעיון שלא יהיה אפשרי לכתיבה (ראה ערך אקונדי ב thunderbird).

לא אמרתי נואש קפצתי לחבר שהוא כותב למערכות PIM כעבודה והוא שימח אותי בכלל : 
כמו שחשבתי עדיף לבצע הפרדה בין סיפריה שתבצע ניהול חיבור ויצרת איש קשר / יומן מאשר לקחת את הקוד ולהשתמש בהתאמה של כל אחד ממנגנוני הPIM.
צריך להזהר מלהשתמש בהתקני האיחסון היפים למידע (המחלקות של KDE , Evolution )  ולהשתמש במחלקות של vcard  , vcal / vcalendar וכן מעטות הסיפריות שמתנהגות בצורה זהה גם בויינדוס וגם בדביאן.

בנושאי vcard צריך להזהר כי הרבה מאוד חברות מגדירות שדות נוספים שיש צורך לזהות (רמז עבה לסקייפ ו KDE).

לבנתיים אני יושב על ה design אם כי gdata נראה ממש טוב. 

 

יום שני, דצמבר 05, 2011

הצרות שלי עם SOAP

 מאז ומעולם תמיד חיפשנו מערכת שתוכל לדבר בין מערכות שונות,
פעם זה COBRA ופעם BXML לפעמים אנחנו סתם רוצים מידע קריא לאדם.
מפה המצאנו את הרעה החולה הנקראת SOAP בשביל לתת פתרון לRPC שיהיה גם קריא וגם נוח.

ישנם היום הרבה דרכים להשתמש ב RPC על גבי רשת (ידוע גם כ webservices) זה יכול REST הישן והטוב (פשוט תממשו url לכל בקשה).
זה יכול להיות XML-RPC המצויין לדעתי.
אבל לצערי המון פעמים רואים שימוש ב SOAP בלבד , ותמיכה בסוג אחד של סביבה נתמכת.

הסיבה לפחות בארץ היא שליטת אחת מסביבות הפיתוח בשוק (מישהוא אמר .NET ?) ושם כברירת מחדל מופיע בשימוש ב SOAP (אם מישהוא רוצה להעביר מידע הדרך הפשוטה בשבילו ביותר היא SOAP) , אם לפני שנים אנשים למדו והבינו עם מה עובדים היום יש כמות אדירה של פקידים שלא זזים מילמיטר מהמקום הבטוח שלהם שפשוט מושכים תפריטים.
לא פעם שמעתי תגובות מה איך אתה משתמש ב webservices מתוך C++ ואיך בכלל אתה משתמש ב telnet - זה בטח האקינג... 

והחלק הנורא ביותר - הwsdl שלך אי אפשר לעבוד איתו.
לקח לי שבועות להבין שהרבה פקידים פשוט לא מזיזים את התוכס שלהם בשביל לכתוב קוד נכון שידע לעבוד גם עם encoded וגם עם literal.
אתם חושבים שזה הכול ? 
מתברר שהרבה צועקים שאי אפשר להשתמש ב soap ולהחזיר תשובה כאשר זו לא נמצאת בתוך  בקונטיינר (יש אלמנט תשובה ובתוכו עוד כלל התשובות שלכם).
משום מה ברירת המחדל של יצרת complex type אומרת שכולם צריכים לעבוד לפי החליל של MS (מי שעובד ב Java מקלל במיוחד את יצור הwsdl ).
לאלה מאיתנו שכותבים קוד ב C++ קל יותר יש לנו מספר חבילות שדואגות לנושא (הממ .. gsoap + libxml) , ברגע שיש דרישה אנחנו פשוט משנים את הפורמט בלי יותר מדי כאב ראש.

על כן יש לי (ולהרבה אחרים) כלל - 
במהלך ההכנה לחוזה אנו כותבים כי כלל הממשקים חייבים להציג את כי פעולת התקשורת אפשרית באמצעות אפליקציה צד שלישי מוכרת (soapui/telnet).

זה נשמע לכם כמשהוא מוזר אבל לפעמים מגלים שצריך איזשהוא dll בשביל לבצע עוד תקשורת ועיבוד של המידע (הממ... crms המממ...). כן כן אבל ה webserivce שלי דורש NTLMv2 יגיד המתכנת (לדוגמה בdynamics) - למצב כזה אנחנו יכולים להתמודד שלauthntication נדאג אחרי זה (כי gsoap לדוגמה מאפשר זאת ) וחוץ מזה שכבר זונחים את NTLM ועוברים לkerberos.
בגלל זה עדיף לחלק את העבודה בין עבודה עם רק webservices לעובדה עם מכלול אפליקציות אחרות שצריך לתקשר איתם.

הרי בשביל מה יש ממשק וובי / webservices אם צריך עוד אפליקציה בשביל לעבוד זה (ראה ערך dynamics crm / owa בלי outlook שמותקן על המחשב המקומי).

אז מה קורה כאשר יש לנו חברה שלא ממש רוצה לעזור למתכנתים לעבוד עם ה CRM שלה ?
נכון אנחנו נתקעים מצד אחד עם קוד שהוא בעייתי לעבודה (אי אפשר להבין למה השרת) ומצד שני כאשר אנו כבר יוצרים קשר עם מישהוא הוא לא מסוגל לעזור בשביל לפתור את הבעייה.

(עדיין תקוע עם webservice בשביל לגרום לCRM לעבוד עם akonadi :-( )

יום שני, נובמבר 28, 2011

Inserting LONGVARBINARY into MS SQL Server

נתקעתי בבעיה קטנה של המרת קוד ישן שעובד עם freetds + unixodbc בשביל להכניס מידע לבסיס נתונים של MS SQL.
כזכור הסוג IMAGE יורד בהקדם אז צריך לעשות המרה של הקוד שמטפל בזה לאחרים.
אז הנה דוגמה לשימוש ב כfreetds ע"ג odbc בשביל לשלוח מידע שיכול לאחסן קבצים.


/*

    Copyright 2011  Boris Shtrasman boris sh 1983 at gmail dot com

    Permission to use, copy, modify, and distribute this software
    and its documentation for any purpose and without fee is hereby
    granted, provided that the above copyright notice appear in all
    copies and that both that the copyright notice and this
    permission notice and warranty disclaimer appear in supporting
    documentation, and that the name of the author not be used in
    advertising or publicity pertaining to distribution of the
    software without specific, written prior permission.

    The author disclaim all warranties with regard to this
    software, including all implied warranties of merchantability
    and fitness.  In no event shall the author be liable for any
    special, indirect or consequential damages or any damages
    whatsoever resulting from loss of use, data or profits, whether
    in an action of contract, negligence or other tortious action,
    arising out of or in connection with the use or performance of
    this software.

*/
#include <stdio.h>
#include <sql.h>
#include <sqlext.h>

#define TABLE_NAME "binary_v3"

static void extract_error(
    char *fn,
    SQLHANDLE handle,
    SQLSMALLINT type);



int main()
{
   #undef UNICODE /* for the Win ppl */
   SQLHENV henv;
   SQLHDBC dbc;
   SQLHSTMT stmt;

   RETCODE retcode;

   SQLLEN cbTextSize, lbytes;
   PTR pParmID;
   char binary_data[8024]="";

   memset(binary_data,0,sizeof(binary_data));
   binary_data[1] = 5;

   //lets define some strage number for batch  
   SDWORD batch_size = sizeof(binary_data) - 1024;

   // Allocate the ODBC environment and save handle.
   retcode = SQLAllocHandle (SQL_HANDLE_ENV, NULL, &henv);
   if ( (retcode != SQL_SUCCESS_WITH_INFO) && (retcode != SQL_SUCCESS)) 
   {
      printf("SQLAllocHandle(Env) Failed\n\n");
      goto cleanup;
   }

   // Notify ODBC that this is an ODBC 3.0 app.
   retcode = SQLSetEnvAttr(henv, 
        SQL_ATTR_ODBC_VERSION,
           (SQLPOINTER) SQL_OV_ODBC3,
                SQL_IS_INTEGER);

   if ( 
       (retcode != SQL_SUCCESS_WITH_INFO) && (retcode != SQL_SUCCESS)) 
   {
      printf("SQLSetEnvAttr(ODBC version) Failed\n\n");
      goto cleanup;
   }

   // Allocate ODBC connection handle and connect.
   retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &dbc);
   if ( (retcode != SQL_SUCCESS_WITH_INFO) && (retcode != SQL_SUCCESS))
   {
      printf("SQLAllocHandle(dbc) Failed\n\n");
      goto cleanup;
   }
     
   SQLCHAR odbc_dsn_string_dump[255];
   SQLSMALLINT odbc_dsn_string_dumpLen;

   retcode = SQLDriverConnect(
           dbc,
           NULL,
           (SQLCHAR *)"Driver=FreeTds;"
                "SERVER=192.168.1.2\\SQLEXPRESS;UID=sa;"
                "PWD=pwd;DATABASE=db",
           SQL_NTS,
           odbc_dsn_string_dump,
           sizeof(odbc_dsn_string_dump),
           &odbc_dsn_string_dumpLen,
           SQL_DRIVER_COMPLETE);
   
   if ( (retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO) ) 
   {
      extract_error("Describe param",dbc, SQL_HANDLE_DBC);  
       
      goto cleanup;
   }

   // Allocate statement handle.
   retcode = SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt);
   if ( (retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO) ) 
   {
      extract_error("Allocation ... ",stmt, SQL_HANDLE_STMT);  
       
      goto cleanup;
   }

   //tell the driver to work only in sync mode !
   SQLSetStmtOption(dbc, SQL_ASYNC_ENABLE, SQL_ASYNC_ENABLE_OFF);

   if (SQL_SUCCESS !=  SQLExecDirect(stmt,  (SQLCHAR *)
            "IF EXISTS "
            "(SELECT * FROM sysobjects WHERE id = "
            "object_id(N'[dbo].[" TABLE_NAME "]') "
            "AND OBJECTPROPERTY(id, N'IsUserTable') = 1) "
            "DROP TABLE [dbo].[" TABLE_NAME "]; " 
            "CREATE TABLE [dbo].[" TABLE_NAME "] "
            "( name varchar(256), content varbinary(max) NOT NULL ); ",    
            SQL_NTS))
   {
      extract_error("drop + create table ... ",stmt, SQL_HANDLE_STMT);  
       
      goto cleanup;
 
   }
   
   lbytes = (SDWORD) sizeof(binary_data);
   //we are setting the length to be calculated on run time.
   cbTextSize = SQL_LEN_DATA_AT_EXEC(lbytes);

   // Bind the parameter marker.
   
   SQLSMALLINT    binded_column_type,
                  persicion_in_binded_column,
                  is_binded_type_nullable;
   
   SQLUINTEGER   binded_column_size_from_driver = 0;
   
   retcode = SQLPrepare( stmt,
                         (SQLCHAR*)"INSERT INTO " 
                                    TABLE_NAME 
                        " (name, content) VALUES ('file', ?)",SQL_NTS);

#if 0

//for now freetds does not support describe param 
// but on the second it does I must use describe
//      param inorder to get the correct sizes and column types
   retcode = SQLDescribeParam(stmt,
         1,
         &binded_column_type,
         &binded_column_size_from_driver,
         &persicion_in_binded_column,
         &is_binded_type_nullable);

   printf ("Desc info (%d) %d %d %d %d\n",retcode , binded_column_type , 
binded_column_size_from_driver, persicion_in_binded_column, is_binded_type_nullable);

   if (binded_column_type != SQL_LONGVARBINARY) 
   {
        printf("This is strange got %d instead of %d\n" , 
binded_column_type, SQL_LONGVARBINARY);
   }

   if (SQL_SUCCESS != retcode ) 
   {
     extract_error("Describe param",stmt, SQL_HANDLE_STMT);  
         goto cleanup;
   }
#else
    binded_column_size_from_driver = 0;//Don't ask me why freetds\
 fails to work with the 2G size 
#endif

   retcode = SQLBindParameter(stmt, 
                              1, 
                              SQL_PARAM_INPUT,
                              SQL_C_BINARY,
                              SQL_LONGVARBINARY,
                              binded_column_size_from_driver
                                        /*2147483647 vs 32K*/,
                              0,
                              (VOID *) 1/* any value other then null*/,
                              0,
                              &cbTextSize);
    
   if ( (retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO) ) {
      extract_error("Bind param param",stmt, SQL_HANDLE_STMT);  
      goto cleanup;
   }

   // Execute the command.
   retcode = SQLExecDirectA(
                stmt, 
                (SQLCHAR *)"INSERT INTO " 
                            TABLE_NAME 
                           " (name, content) VALUES ('file', ?)", SQL_NTS);
   
   if ( 
        (retcode != SQL_SUCCESS) 
           &&
        (retcode != SQL_NEED_DATA)
           && 
        (retcode != SQL_SUCCESS_WITH_INFO) )
   {
      extract_error("Insert",stmt, SQL_HANDLE_STMT);  
      goto cleanup;
   }

   // We set up the data len to be computed on executing therefor we must \
         actually send the data.
   // prior to calling put data you must first call to SQLParamData .. \
         why, only the odbc masters know 
   retcode = SQLParamData(stmt, &pParmID);
   printf("SQLParam Data = %d \n",retcode);

   if (SQL_NEED_DATA != retcode)
   {
      extract_error("grrr ... ",stmt , SQL_HANDLE_STMT);
      goto cleanup;
   }

   char * pt_data_be_inserted_into_db = binary_data;
   //we defined that data len will be calculated on executing \
        this is the reason why we got this value
  
   if (retcode == SQL_NEED_DATA) 
   {
      
      while (lbytes > batch_size)
      {

         retcode = SQLPutData(stmt,
                   (SQLCHAR*) pt_data_be_inserted_into_db, batch_size);
         lbytes -= batch_size;
         pt_data_be_inserted_into_db += batch_size;
      }
      // Put final batch.
      retcode = SQLPutData(stmt, pt_data_be_inserted_into_db, lbytes); 
      printf("last put data with %d bytes and exited with %d\n", lbytes,retcode);
   
      if ( 
           (retcode != SQL_SUCCESS)
              && 
           (retcode != SQL_SUCCESS_WITH_INFO) )
      {
         extract_error("Put data for stmt",stmt, SQL_HANDLE_STMT);  
         goto cleanup;
      }
     
   }
  
   // send the data to the db.
   retcode = SQLParamData(stmt, &pParmID);
   if ( (retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO) ) 
   {
      extract_error("Final Param data",stmt, SQL_HANDLE_STMT);  
      goto cleanup;
   }
   
cleanup:
   // Clean up.
   SQLFreeHandle(SQL_HANDLE_STMT, stmt);
   SQLDisconnect(dbc);
   SQLFreeHandle(SQL_HANDLE_DBC, dbc);
   SQLFreeHandle(SQL_HANDLE_ENV, henv);
   return 0;
}

void extract_error(
    char *fn,
    SQLHANDLE handle,
    SQLSMALLINT type)
{
    SQLINTEGER     i = 0;
    SQLINTEGER     native;
    SQLCHAR     state[ 7 ];
    SQLCHAR     text[256];
    SQLSMALLINT     len;
    SQLRETURN     ret;

    fprintf(stderr,
            "\n"
            "The driver reported the following diagnostics whilst running "
            "%s\n\n",
            fn);

    perror("Last os error :");
    do
    {
        ret = SQLGetDiagRec(type, handle, ++i, state, &native, text,
                            sizeof(text), &len );
        if (SQL_SUCCEEDED(ret))
            printf("%s:%ld:%ld:%s\n", state, i, native, text);
    }
    while( ret != SQL_NO_DATA );
    fprintf(stderr, "found %d elements of information\n",(i>0)?i - 1:0);
}