Snowflake ODBC驱动程序无法识别TIMESTAMP_TZ



(1)ODBC中的表describe在Snowflake中返回一个TIMESTAMP_TZ列,其sqltype=93(SQL_TYPE_TIMESTAMP)。TIMESTAMP_TZ列与TIMESTAMP_NTZ列返回所有相同的属性

SELECT  get_ddl('TABLE', 'TS_TEST');

create or replace TABLE TS_TEST (
TS TIMESTAMP_TZ(9),
ID NUMBER(38,0)
);
SELECT column_name, data_type, datetime_precision
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_schema = 'PUBLIC'
and table_name = 'TS_TEST'
and column_name = 'TS';

COLUMN_NAME  DATA_TYPE      DATETIME_PRECISION
-----------  -----------    -------------------
TS           TIMESTAMP_TZ   9

sqlstmt = 0x000000a220befd60 L"SELECT * FROM TS_TEST LIMIT 1"
rc = SQLDescribeColW (*cursor_ptr,
column_index,
(SE_WCHAR FAR *) column_name,
(SNOW_MAX_IDENTIFIER_LEN * sizeof(SE_WCHAR)), /* BufferLength */
&name_length,
&sqltype,
(SQLULEN *) &precision_size,
&scale,
&nulls);

column_name = 0x000000a220bef670 L"TS"
name_length = 2
sqltype = 93  // #define SQL_TYPE_TIMESTAMP  93;  // C:Program Files (x86)Windows Kits10Include10.0.19041.0umsql.h
precision_size = 29 // #define SQL_SF_TIMESTAMP_COLUMN_SIZE  29; C:Program FilesSnowflake ODBC Driverincludesf_odbc.h
scale = 9
nulls = 1

(2)Snowflake ODBC驱动程序文档中关于TIMESTAMP_TZ的内容非常稀少
没有使用ODBC将输入/输出绑定到TIMESTAMP_TZ的示例

Snowflake(Simba)ODBC提供了什么数据结构来绑定输入/输出当该值包括时区偏移信息时,是否将其转换为TIMESTAMP_TZ列?结构在哪里定义

例如:MS SqlServer定义SQL_SS_TIMESTAMPOFFSET_STRUCT,用于绑定C:\Program Files(x86)\Windows Kits\10\Include\10.0.18362.0\um\sqltypes.h 中的DATETIMEOFFSET列

typedef struct tagSS_TIMESTAMPOFFSET_STRUCT {  
SQLSMALLINT year;  
SQLUSMALLINT month;  
SQLUSMALLINT day;  
SQLUSMALLINT hour;  
SQLUSMALLINT minute;  
SQLUSMALLINT second;  
SQLUINTEGER fraction;  
SQLSMALLINT timezone_hour;  
SQLSMALLINT timezone_minute;  
} SQL_SS_TIMESTAMPOFFSET_STRUCT;  

我们应该将TIMESTAMP_TZ列绑定为BINARY(SQL_C_BINARY)还是字符串(SQL_C_WCHAR)?这应该只适用于ODBC 3.5,而不是ODBC 3.8所要求的。目前这是不可行的,因为Snowflake ODBC驱动程序中的函数SQLDescribeColW()将TIMESTAMP_TZ列描述为SQL_TYPE_TIMESTAMP,即与TIMESTAMP_NTZ列相同的类型代码。

因此,ODBC应用程序无法区分TIMESTAMP_TZ和TIMESTAMP_NTZ列。

(3)Snowflake ODBC文档中的以下主题暗示了自定义SQL数据类型,但
没有提供绑定TIMESTAMP_TZ值的示例,也没有提供适当的数据结构:

https://docs.snowflake.com/en/user-guide/odbc-api.html

"Snowflake支持的某些SQL数据类型在中没有直接映射ODBC(例如TIMESTAMP_*tz、VARIANT)。要使ODBC驱动程序使用不支持的数据类型时,随附的头文件驱动程序包括以下自定义数据类型的定义:;

////////////////////////////////////////////////////////////////////////////////////////////////////
/// Custom SQL Data Type Definition
///
///
////////////////////////////////////////////////////////////////////////////////////////////////////
#define SQL_SF_TIMESTAMP_LTZ 2000
#define SQL_SF_TIMESTAMP_TZ  2001
#define SQL_SF_TIMESTAMP_NTZ 2002
#define SQL_SF_ARRAY         2003
#define SQL_SF_OBJECT        2004
#define SQL_SF_VARIANT       2005

参见主题";C数据类型可扩展性";在ODBC文档中

https://learn.microsoft.com/en-us/sql/odbc/reference/develop-app/c-data-types-in-odbc?redirectedfrom=MSDN&view=sql-server-ver15

"在ODBC 3.8中,可以指定驱动程序特定的C数据类型。这使您能够在ODBC中将SQL类型绑定为驱动程序特定的C类型调用SQLBindCol、SQLGetData或SQLBindParameter。这对于支持新服务器非常有用类型,因为现有的C数据类型可能无法正确表示新的服务器数据类型。使用驱动程序特定的C类型可以增加驱动程序可以执行的转换次数。

https://learn.microsoft.com/en-us/sql/odbc/reference/develop-app/driver-specific-data-types-descriptor-information-diagnostic?view=sql-服务器-2017

注意:"驱动程序特定的数据类型、描述符字段、诊断字段、信息类型、语句属性,并且必须在驱动程序文档中描述连接属性。当传递这些值中的任何一个时对于ODBC函数,驱动程序必须检查该值是否有效。驾驶员返回适用于其他驱动程序的驱动程序特定值的SQLSTATE HYC00(未实现可选功能)">

(4)ODBC.ini中是否有要设置的注册表项或注册表项?或在ODBC连接句柄上启用的另一个属性控制与Snowflake自定义数据类型有关的行为?我对TIMESTAMP_TZ、TIMESTAMP_NTZ和TIMESTAMP_LTZ特别感兴趣

我尝试配置参数ODBC_USE_STANDARD_TIMESTAMP_COLUNSIZE根据Snowflake ODBC文档中的以下主题:

https://docs.snowflake.com/en/user-guide/odbc-parameters.html

其他连接参数

ODBC使用标准时间戳列大小

"此布尔参数影响列大小(以字符为单位)为SQL_TYPE_TIMESTAMP返回。当该参数被设置为真时,驱动程序按照ODBC标准返回29。当参数设置为false,驱动程序返回35,这允许有空间用于时区偏移(例如"-08:00")。

此值不仅可以通过odbc.ini文件(Linux或macOS)设置或者Microsoft Windows注册表,还有连接字符串">

但是,以下任何一项都不会对行为产生任何影响

A)在DSN名称下设置注册表项ODBC_USE_STANDARD_TIMESTAMP_COLUNSIZE:

  • (字符串值)设置为FALSE/TRUE
  • (DWORD 32位值)到0/1

B)连接到ODBC连接字符串(基于DSN或无DSN的字符串):

- "ODBC_USE_STANDARD_TIMESTAMP_COLUMNSIZE=FALSE", 
- "ODBC_USE_STANDARD_TIMESTAMP_COLUMNSIZE=TRUE"
- "ODBC_USE_STANDARD_TIMESTAMP_COLUMNSIZE=0", 
- "ODBC_USE_STANDARD_TIMESTAMP_COLUMNSIZE=1"

注意:以上参数对行为没有影响。SQLDescribeColW总是返回完全相同的属性对于TIMESTAMP_TZ和TIMESTAMP_NTZ列。

sqltype = 93;
// #define SQL_TYPE_TIMESTAMP  93;  
// C:Program Files (x86)Windows Kits10Include10.0.19041.0umsql.h
precision_size = 29;
// #define SQL_SF_TIMESTAMP_COLUMN_SIZE  29; 
// C:Program FilesSnowflake ODBC Driverincludesf_odbc.h
// scale = 9;
// nulls = 1;

人们会期望TIMESTAMP_TZ列被描述为//C:\Program Files\Snowflake ODBC Driver\include\sf_ODBC.h中定义的类型,即SQL_SF_TIMESTAMP_TZ(2001)

和TIMESTAMP_NTZ列将被描述为类型SQL_SF_TIMESTAMP_NTZ(2002)

(5)注意:C:\Program Files\Snowflake ODBC驱动程序中安装的SnowflakeDSII.dll版本为2.22.4.0

注意:自发布原始帖子以来,Snowflake ODBC驱动程序已升级到最新版本,即2.24.5.0,行为没有任何变化

/* In the connection, the target ODBC version is set to ODBC 3.8 */

rc = SQLSetEnvAttr (connection->henv,
SQL_ATTR_ODBC_VERSION,
(void *)SQL_OV_ODBC3_80,
0);
rc = SQLGetInfoW (connection->hdbc, SQL_DRIVER_ODBC_VER,
&odbc_ver, SE_MAX_MESSAGE_LENGTH, NULL);
odbc_ver = 0x00000000022cae40 L"03.80"

(6)参数CLIENT_TIMESTAMP_TYPE_MAPPING未设置为任何值。无论如何,它只适用于TIMESTAMP_LTZ或TIMESTAMP_NTZ。我特别感兴趣的是仅绑定TIMESTAMP_TZ

https://docs.snowflake.com/en/sql-reference/parameters.html#client-时间戳类型映射

参数TIMESTAMP_TYPE_MAPPING设置为其默认值。无论如何,它指定TIMESTAMP数据类型别名映射到的TIMESTAMP_*变体。测试场景显式创建TIMESTAMP_TZ列,并且不使用别名。

我通过使用获得所有三种Snowflake正确的数据类型

SQLLEN nDataType = SQL_UNKNOWN_TYPE;
rc = ::SQLColAttribute(
hstmt,
nCol,
SQL_DESC_CONCISE_TYPE,
NULL,
0,
NULL,
&nDataType);

似乎目前没有特定于数据类型的结构可用于SQL_SF_TIMESTAMP_TZ,该结构提供为记录存储的时区。如果您将SQL_SF_TIMESTAMP_TZ数据绑定为常规文本,则不确定Snowflake驱动程序是否会返回时区,但可能值得尝试。

最新更新