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

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

תקלות קידוד שפה בבסיס נתונים PostgreSql.

תקלות קידוד שפה בבסיס נתונים  PostgreSql .

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

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

הקורא הממוצע ישאל מה יש לכותב כנגד שפות שהן לא אנגלית אמריקאית ?

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

מדוע אני בתור איש וינדוס אומר להמנע משימוש בוינדוס כמערכת עבור בסיס נתונים במקרה הזה ?
התמיכה בקידודים ו locale שאינם en_us לדעתי לוקה בחסר, כברירת מחדל אין תמיכה ביוניקוד (צריך להריץ chcp 65001 בשביל להפעיל תמיכה למשל ביניקוד).

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

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

הבעיה היא שבדר"כ יש יותר מדרך אחת להציג מידע ובחירת השפה "עברית" בצד שרת ובחירת שפה "עברית" בצד לקוח לא באמת מחייב שהמידע יעבור תקין.

סיבה קלאסית היא הסתמכות על הקידוד מערכת ההפעלה ושימוש בקידוד ברירת מחדל, כשאנו למשל מדברים על עברית זה יכול להיות : UTF16* , ISO-8859-8, UTF8, CP1255,CP 862 ,ISO646.

ממה שאני ראיתי לרוב באפליקציות מהעשור האחרון דובר על CP122 ו iso8859-8 (לא נתקלתי במצב הגיוני של UTF16 או 8 כברירת מחדל עד היום).

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

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


אם יש לנו גישה לקוד המקור נוכל להגדיר את הקידוד ברמת ה session (במקרה של ODBC) -
לדוגמה עבור PG ניתן להגדיר את הקידוד בצורה הבאה :

"SET client_encoding= 'UTF8'"

ואז לשלוח את המידע כשהוא עבר קידוד ל UTF8 (כל מחרוזת שנשלח צריכה לעבור קידוד ישירות ל UTF8 לפני ההגעה לקו)

פתרון נוסף הוא בחירת הקידוד ברמת החיבור בהנחה שהדריבר שאנו עובדים איתו תומך בunicode :

std::string url = "odbc:postgresql://url:ip/databse&client_encoding=UTF8"

חשוב לציין שאם מחרוזת החיבור נובעת מ DSN אז אפשר לבצע את ההחלפה גם בתוך ה DSN עצמו.

במידה ומשתמשים ב DSN תחת FreeTDS שם הפרמטר הוא ClientCharset והקידוד הוא לפי iconv ובגרסה הקניינית שם הפרמטר הוא Charset.
אצל אורקל שם הארגומנט הוא DatabaseCharacterSet


במידה במידה ואנו עובדים עם קידוד פנימי של שני בתים (wchar_t/UCS2)  ולא יכולים לשנות את מחרוזת החיבור הוא להחליף את הדריבר הרגיל לדוגמה psqlodbc.so ל psqlodbcw.so) ולהתבסס על המרת ברירת מחדל (בגרסאות ישנות של posgresql הפתרון היה פשוט להשתמש ב UCS2 ל UCS2 אם גם השרת וגם הלקוח הם עובדים על וינדוס).

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

המרה  ברמת הפעולה בבסיס הנתונים תראה בערך כך :

convert ( text , 'sourceencoding' , 'destencoding')

הממרה ברמת השפה איתה אנו משתמשים תראה בערך כך :

Perl:
$internalEncoding = decode( 'iso-8859-8', $var );
$result = encode( 'utf-8', $internalEncoding );
Java:
Charset utf8 = Charset.forName("UTF-8");
Charset iso8859_8 = Charset.forName("ISO-8859-8");
CharBuffer data = iso8859_8.decode(inputBuffer);
ByteBuffer output = utf8.encode(data);


iconv - C:

iconv_t cnv = iconv_open("UTF-8", "ISO_8859-8"); if (-1 == cnv) { return ; }
int ret = iconv(cnv, &text_in_8859_8, &length_of_text_in_8859_8, &text_in_utf8, &length_of_text_in_utf8);


עבודה עם PG דרך ODBC

כשעובדים עם בסיס נתונים אפשר לעבוד קשה ולהשתמש בפקודות הספציפיות לבסיס הנתונים (כמו למשל PGConnect). ואפשר לעבוד בשיטה הסטראדנרטית שימוש ב ODBC.

בשביל לגשת ולהשתמש ב PG תחת ODBC קודם כל נגדיר DSN עבורו בדביאן קודם נתקין את הדריבר (מתוך  odbc-postgresql)

 זה יראה כך במקרה של UNICODE:

[otherdb]
Description         = PostgreSQL connection to otherdb
Driver              = PostgreSQL Unicode
Database            = otherdb
Servername          = 192.168.1.35
UserName            = 
Password            = 
ReadOnly            = No
RowVersioning       = No
ShowSystemTables    = No
Trace               = No 
#uncomment for traces
#TraceFile           = /tmp/psqlodbc.log 

לאחר מכן בקוד שלנו נוכל להתחבר באמצעות התחביר הבא :

"DSN=otherdb;DATABASE=otherdb;UID=%s;PWD=%s"  
את ה %s כמובן מחליפים לפי הנתונים הנכונים.

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

ביצוע שאילתה מ 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);
}

יום שני, אפריל 18, 2016

תמונה אחת שווה דיווח באג

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