11
11
{-# LANGUAGE RankNTypes #-}
12
12
{-# LANGUAGE FlexibleContexts #-}
13
13
{-# LANGUAGE ForeignFunctionInterface #-}
14
+ {-# LANGUAGE CPP #-}
14
15
15
16
-- | ODBC database API.
16
17
--
@@ -62,7 +63,14 @@ import Data.Int
62
63
import Data.String
63
64
import Data.Text (Text )
64
65
import qualified Data.Text as T
66
+ #if MIN_VERSION_text(2,0,0)
67
+ import qualified Data.ByteString.Internal as SI
68
+ import qualified Data.Text.Encoding.Error as T
69
+ import qualified Data.Text.Encoding as T
70
+ #else
65
71
import qualified Data.Text.Foreign as T
72
+ import Data.Text.Foreign (I16 )
73
+ #endif
66
74
import Data.Time
67
75
import Foreign hiding (void )
68
76
import Foreign.C
@@ -399,7 +407,7 @@ withExecDirect dbc string params cont =
399
407
(assertSuccessOrNoData
400
408
dbc
401
409
" odbc_SQLExecDirectW"
402
- (T. useAsPtr
410
+ (useAsPtrCompat
403
411
string
404
412
(\ wstring len ->
405
413
odbc_SQLExecDirectW
@@ -449,7 +457,7 @@ withBindParameter dbc parameter_number param cont statement_handle = go param
449
457
go =
450
458
\ case
451
459
TextParam text ->
452
- T. useAsPtr -- Pass as wide char UTF-16.
460
+ useAsPtrCompat -- Pass as wide char UTF-16.
453
461
text
454
462
(\ ptr len_in_chars ->
455
463
runBind
@@ -587,7 +595,7 @@ fetchStatementRows dbc stmt = do
587
595
-- | Describe the given column by its integer index.
588
596
describeColumn :: Ptr EnvAndDbc -> SQLHSTMT s -> Int16 -> IO Column
589
597
describeColumn dbPtr stmt i =
590
- T. useAsPtr
598
+ useAsPtrCompat
591
599
(T. replicate 1000 (fromString " 0" ))
592
600
(\ namep namelen ->
593
601
(withMalloc
@@ -618,7 +626,7 @@ describeColumn dbPtr stmt i =
618
626
digits <- peek digitsp
619
627
isnull <- peek nullp
620
628
namelen' <- peek namelenp
621
- name <- T. fromPtr namep (fromIntegral namelen')
629
+ name <- fromPtrCompat namep (fromIntegral namelen')
622
630
evaluate
623
631
Column
624
632
{ columnType = typ
@@ -930,12 +938,13 @@ getBinaryData dbc stmt column = do
930
938
-- | Get the column's data as a text string.
931
939
getTextData :: Ptr EnvAndDbc -> SQLHSTMT s -> SQLUSMALLINT -> IO Value
932
940
getTextData dbc stmt column = do
941
+ -- We need to fetch as UTF-16LE (see callsite), then convert to Text
933
942
mavailableChars <- getSize dbc stmt sql_c_wchar column
934
943
case mavailableChars of
935
944
Just 0 -> pure (TextValue mempty )
936
945
Nothing -> pure NullValue
937
946
Just availableBytes -> do
938
- let allocBytes = availableBytes + 2
947
+ let allocBytes = availableBytes + 2 -- room for NULL
939
948
withMallocBytes
940
949
(fromIntegral allocBytes)
941
950
(\ bufferp -> do
@@ -947,7 +956,7 @@ getTextData dbc stmt column = do
947
956
column
948
957
(coerce bufferp)
949
958
(SQLLEN (fromIntegral allocBytes)))
950
- t <- T. fromPtr bufferp (fromIntegral (div availableBytes 2 ))
959
+ t <- fromPtrCompat bufferp (fromIntegral (div availableBytes 2 ))
951
960
let ! v = TextValue t
952
961
pure v)
953
962
@@ -1433,3 +1442,38 @@ sql_c_time = coerce sql_time
1433
1442
-- <https://docs.rs/odbc-sys/0.6.3/odbc_sys/constant.SQL_SS_LENGTH_UNLIMITED.html>
1434
1443
sql_ss_length_unlimited :: SQLULEN
1435
1444
sql_ss_length_unlimited = 0
1445
+
1446
+
1447
+ #if MIN_VERSION_text(2,0,0)
1448
+ type I16 = Int
1449
+ #endif
1450
+
1451
+ -- FIXME fail with Randomized with seed 1862667972
1452
+ -- (on 9.2 as well)
1453
+
1454
+ -------- 'T.fromPtr' but compatible with text v1 and v2
1455
+
1456
+ fromPtrCompat :: Ptr Word16 -> I16 -> IO Text
1457
+ #if MIN_VERSION_text(2,0,0)
1458
+ fromPtrCompat bufferp len16 = do
1459
+ let lenBytes = len16 * 2
1460
+ noFinalizer = return () -- N.B. inner bufferp is 'free'd after this withMallocBytes block
1461
+ -- invariant: this does no additional allocation
1462
+ tempBS <- S. unsafePackCStringFinalizer (castPtr bufferp) lenBytes noFinalizer
1463
+ -- invariant: this makes a copy:
1464
+ return $! T. decodeUtf16LEWith T. strictDecode tempBS
1465
+ #else
1466
+ fromPtrCompat = T. fromPtr
1467
+ #endif
1468
+
1469
+ useAsPtrCompat :: Text -> (Ptr Word16 -> I16 -> IO a ) -> IO a
1470
+ #if MIN_VERSION_text(2,0,0)
1471
+ useAsPtrCompat t cont16 = do
1472
+ let (fp8, len8) = SI. toForeignPtr0 $ T. encodeUtf16LE t
1473
+ fp16 = castForeignPtr fp8
1474
+ len16 = len8 `div` 2
1475
+ withForeignPtr fp16 $ \ p16 ->
1476
+ cont16 p16 (fromIntegral len16)
1477
+ #else
1478
+ useAsPtrCompat = T. useAsPtr
1479
+ #endif
0 commit comments