יום שבת, אוקטובר 15, 2011

החיים הקשים ב C עם Unicode ו ODBC

לאחרונה נפלה בחלקי ההזדמנות לספק תמיכה  מרובת שפות למערכת O/RM.

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

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

הבעיה היתה איך אני מאחסן ב unicode בבסיס הנתונים בצורה כזו שגם האפליקציה שלי וגם אפליקציות אחרות יראו את אותו התוכן:
התשובה הנאיבית אומרת בואו נשתמש ב utf8 ונשלח את המידע ב SQLExecDirectA ביחד עם SQL_C_CHAR ונגדיר איכשהוא שבסיס הנתונים יקבל ויעבוד.
הבעיה עם ההנחה הנאבית שלי היא שאמנם זה עובד ב MySQL ו Postgresql ב SQL Server זה לא עובד (יותר נכון אין לי מושג איך לבצע זאת אם בכלל).

מה שלמעשה יקרה שהמידע יאוחסן בצורת utf8 ורק אנשים שיתייחסו למידע כ utf8 יקבלו את המידע - כי SQL Server עובד על אחד הווריאציות של  UTF-16.

טוב בסדר נניח וזה מציק למה לא להשתמש ב CONVERT ולהפוך את המידע
ע"ג בסיס הנתונים ..
אבל באמת נריץ אלפי פקודות שמכילות convert ?!  אמנם טכנית זה אפשרי (כי גם ככה אני נמצא בקרביים של הספרייה

טוב הניסיון הבא יהיה לבצע המרה לפני כניסה ל utf16 שימוש ב SQLExecDirectW + SQL_C_WCHAR הבעיה כל הקוד במערכת שלנו משתמש ב utf8 (הלוגים עובדים , עבודה עם קבצים נכונה , קידוד נכון שנשלח ומתורגם) אבל בסופו של דבר הוספנו מקטע רציני:
טופס זמן מעבד (כל קריאה ל MultiByteToWideChar/iconv היא סיפור).
אמנם המידע שיווצר ע"י multibytetowidechar מ CP_UTF8 יהיה קטן מגודל מקסימלי של statment אבל הסיכוי שבטעות נבדוק על מחרוזת wchar_t במקום על מחרוזת ה multibyte הוא מסוכן למדי.

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

הפתרון שבחרתי בסופו של יום :
בגלל מבנה הספריה יש לי יכולת לדעת בכל נקודת זמן האם יש שימוש בתווים לא latin-1.
שימוש בדגל שמאפשר הפעלת המרה לSQL_C_WCHAR רק כאשר יש באמת מידע שהוא דורש המרה.
המרה רק בחלק האחרון לפני כתיבה - כי הדריבר עצמו הוא בעייתי.

נ.ב. - אם זה לא היה ברור עד כו מדובר על קוד שקשור ל windows.

תגובה 1:

  1. טוב, משתתף בצערך... ברור הבא לעולם הדפוק של Unicode בחלונות.

    מספר הערות קטנות:

    - FreeTDS/ODBC לא תומך ב־Wide-Characters כך שהמצב בלינוקס לא מאפשר לתקשר ב־Unicode עם MS SQL Server. מצד שני יש דרייברים לא חופשיים עבור לינוקס/odbc ו־MS SQL שכן תומכים ב־UTF-8 גם MS הבטיחו לשחרר דרייבר שתומך בו לינוקס.

    אם אתה יכול לעבוד עם ספריות C++‎ אני אמליץ לך על CppDB שלי שיודע להפוך **בפנים** UTF-8 ל־UTF-16 לעבוד עם SQL Server ב־UTF-8 וגם בחלונות! ראה: http://art-blog.no-ip.info/sql/cppdb/odbc.html

    השבמחק