JavaScript 存储过程 API

本主题介绍 JavaScript API for Snowflake 存储过程。此 API 由 JavaScript 对象和这些对象中的方法组成。

本主题内容:

对象:snowflake

默认情况下,存储过程中的 JavaScript 代码可以访问 snowflake 对象;您不需要创建此对象。此对象包含存储过程 API 中的方法。例如:

create procedure stproc1()
  returns string not null
  language javascript
  as
  -- "$$" is the delimiter for the beginning and end of the stored procedure.
  $$
  // The "snowflake" object is provided automatically in each stored procedure.
  // You don't need to create it.
  //         |||||||||
  //         vvvvvvvvv
  var statement = snowflake.createStatement(...);
  ...
  $$
  ;
Copy

使用存储过程 中提供了更丰富的代码示例。

常量

无。

方法

addEvent(name[, attributes])

添加用于跟踪的事件。

有关使用 JavaScript 时的跟踪事件的更多信息,请参阅 使用 JavaScript 发出跟踪事件

参数:

name

要添加的事件的名称。

attributes

指定要与事件关联的属性的对象。

错误:

如果出现以下情况,则会引发 JavaScript 错误:

  • name 不是字符串。

  • 实参为零个或两个以上。

示例:

添加一个具有 scorepass 属性的 my_event 事件。

snowflake.addEvent('my_event', {'score': 89, 'pass': true});
Copy
createStatement(sql_command_object)

创建一个 Statement 对象并返回它。该对象的 execute() 方法可以稍后执行。

参数:

sql_command_object

输入参数是一个 JSON 对象(字典),其中包含要执行的语句的文本,以及应绑定到该语句的任何值。

返回:

Statement 对象。

错误:

如果出现以下情况,则会引发 JavaScript 错误:

示例:

此示例不绑定任何值:

var stmt = snowflake.createStatement(
   {sqlText: "INSERT INTO table1 (col1) VALUES (1);"}
   );
Copy

此示例绑定值:

var stmt = snowflake.createStatement(
   {
   sqlText: "INSERT INTO table2 (col1, col2) VALUES (?, ?);",
   binds:["LiteralValue1", variable2]
   }
);
Copy

有关绑定的更多信息(包括其他示例),请参阅 绑定变量

execute(command)

执行 SQL 命令。

参数:

输入与 createStatement() 方法的输入相同。

返回:

ResultSet 对象形式的结果集。

错误:

如果出现以下情况,则会引发 JavaScript 错误:

  • 执行查询时发生错误,例如编译错误。

  • sqlText 缺失或包含空的查询文本。

  • 语句尝试绑定数据类型不受支持的实参。有关数据类型映射的信息,请参阅 SQL 和 JavaScript 数据类型映射。有关绑定的更多信息(包括其他示例),请参阅 绑定变量

备注

execute() 方法(例如 snowflake.execute())与 Statement 对象中的方法(例如 Statement.execute())不完全相同。

log(level, message)

以指定的严重性级别记录消息。

参数:

level

记录消息的严重性级别。您可以指定以下其中一种字符串:

  • 'off'

  • 'trace'

  • 'debug'

  • 'info'

  • 'warn'

  • 'error'

  • 'fatal'

message

要记录的消息。

错误:

如果出现以下情况,则会引发 JavaScript 错误:

  • level 不是字符串。

  • level 不是上面列出的受支持的 level 值之一。

示例:
snowflake.log("error", "Error message");
Copy
setSpanAttribute(key, value)

在跟踪事件时设置当前跨度的属性。

有关使用 JavaScript 时的跟踪事件的更多信息,请参阅 使用 JavaScript 发出跟踪事件

参数:

key

属性的键。

value

属性的值。

错误:

如果出现以下情况,则会引发 JavaScript 错误:

  • 未指定两个实参。

  • key 不是字符串。

示例:

设置一个属性,其键为 example.boolean,其值为 true

snowflake.setSpanAttribute("example.boolean", true);
Copy

对象:Statement

存储过程 Statement 对象提供一些方法,用于执行查询语句和访问有关该语句的元数据(如列数据类型)。

在创建 Statement 对象时,将会解析 SQL 并创建预处理语句。

常量

无。

方法

execute()

此方法执行存储在此 Statement 对象中的预处理语句。

参数:

无需参数,原因是此方法使用已存储在 Statement 对象中的信息。

返回:

ResultSet 对象形式的结果集。

错误:

如果查询失败,则会引发 JavaScript 错误。

示例:

请参阅 使用存储过程

备注

execute() 方法(例如 Statement.execute())与 snowflake 对象中的方法(例如 snowflake.execute())不完全相同。

snowflake.execute(statement_in_JSON_form) 需要一个参数,即要执行的 SQL 语句。Statement.execute() 不需要参数;它使用在创建 Statement 对象时指定的 SQL 语句。

getColumnCount()

此方法返回已执行查询的结果集中的列数。如果尚未执行查询,此方法会引发错误。

参数:

无。

返回:

列数。

错误:

如果语句尚未执行(因此不一定能够确定返回的列数),则会引发 JavaScript 错误。

示例:
var column_count = statement.getColumnCount();
Copy
getColumnName(colIdx)

此方法返回指定列的名称。

参数:

列的索引号(从 1 而不是 0 开始)。

返回:

列的名称。

错误:

如果出现以下情况,则会引发 JavaScript 错误:

  • 尚未执行 Statement

  • 不存在具有指定索引的列。

getColumnScale(colIdx)

此方法返回指定列的小数位数。小数位数是指小数点后的位数。列的小数位数已在 CREATE TABLE 或 ALTER TABLE 语句中指定。例如:

create table scale_example  (
    n10_4 numeric(10, 4)    // Precision is 10, Scale is 4.
    );
Copy

尽管可以针对任何数据类型调用此方法,但它原定用于数值数据类型。

参数:

您想获得其小数位数的列的索引(从 1 而不是 0 开始)。

返回:

列的小数位数(对于数值列);对于非数值(列)为 0

错误:

如果出现以下情况,则会引发 JavaScript 错误:

  • 尚未执行 Statement

  • 不存在具有指定索引的列。

示例:

请参阅 /developer-guide/stored-procedure/stored-procedures-usage`(搜索 :code:`getColumnScale())。

getColumnSqlType(colIdx|colName)

此方法返回指定列的 SQL 数据类型。

参数:

列的索引号(从 1 而不是 0 开始)或列的名称。(此方法会被重载以接受不同的数据类型作为参数。)

列名应全部大写,除非在创建表时在列名中使用了双引号(即保留列名的大小写)。

返回:

列的 SQL 数据类型。

错误:

如果出现以下情况,则会引发 JavaScript 错误:

  • 尚未执行 Statement

  • 不存在具有指定名称或索引的列。

getColumnType(colIdx|colName)

此方法返回指定列的 JavaScript 数据类型。

参数:

列的索引号(从 1 而不是 0 开始)或列的名称。(此方法会被重载以接受不同的数据类型作为参数。)

列名应全部大写,除非在创建表时在列名中使用了双引号(即保留列名的大小写)。

返回:

列的 JavaScript 数据类型。

错误:

如果出现以下情况,则会引发 JavaScript 错误:

  • 尚未执行 Statement

  • 不存在具有指定索引或名称的列。

getNumDuplicateRowsUpdated()

此方法返回此语句更新的“重复”行(通常称为*多重联接行*)的数量。(有关如何形成多重联接行的信息,请参阅 UPDATE 语句的使用说明和示例。)

参数:

无。

返回:

数字类型的值,该值指示更新的多重联接行数。

错误:

如果此语句尚未执行,则会引发 JavaScript 错误。

getNumRowsAffected()

此方法返回受此语句影响(例如插入/更新/删除)的行数。

如果应用了多种类型的变更(例如,MERGE 操作插入了一些行并更新了另一些行),则该数字是受所有变更影响的总行数。

参数:

无。

返回:

数字类型的值,该值指示受影响的行数。

错误:

如果此语句尚未执行,则会引发 JavaScript 错误。

getNumRowsDeleted()

此方法返回此语句删除的行数。

参数:

无。

返回:

数字类型的值,该值指示删除的行数。

错误:

如果此语句尚未执行,则会引发 JavaScript 错误。

getNumRowsInserted()

此方法返回此语句插入的行数。

参数:

无。

返回:

数字类型的值,该值指示插入的行数。

错误:

如果此语句尚未执行,则会引发 JavaScript 错误。

getNumRowsUpdated()

此方法返回此语句更新的行数。

参数:

无。

返回:

数字类型的值,该值指示更新的行数。

错误:

如果此语句尚未执行,则会引发 JavaScript 错误。

getRowCount()

此方法返回已执行查询的结果集中的行数。如果尚未执行查询,此方法会引发错误。

参数:

无。

返回:

行数。

错误:

如果语句尚未执行(因此无法确定返回的行数),则会引发 JavaScript 错误。

示例:
var row_count = statement.getRowCount();
Copy
getQueryId()

此方法返回最近执行的查询的 UUID。

参数:

无。

返回:

包含 UUID 的字符串,即查询 ID。

错误:

如果此语句尚未执行任何查询,则此方法将引发错误“Statement is not executed yet”。

示例:
var queryId = statement.getQueryId();
Copy
getSqlText()

此方法返回 Statement 对象中的预处理查询文本。

参数:

无。

返回:

预处理查询文本的字符串。

错误:

无。

示例:
var queryText = statement.getSqlText();
Copy
isColumnNullable(colIdx)

此方法返回指定的列是否允许 SQL NULL 值。

参数:

列的索引(从 1 而不是 0 开始)。

返回:

如果列允许 SQL NULL 值,则为 true,否则为 false

错误:

如果出现以下情况,则会引发 JavaScript 错误:

  • 尚未执行 Statement

  • 不存在具有指定索引的列。

isColumnText(colIdx)

如果列数据类型是下列 SQL 文本数据类型之一,则此方法返回 true:

  • CHAR 或 CHAR(N),以及它们的同义词 CHARACTER 和 CHARACTER(N)

  • VARCHAR 或 VARCHAR(N)

  • STRING

  • TEXT

否则,它将返回 false。

参数:

列的索引(从 1 而不是 0 开始)。

返回:

如果列数据类型是 SQL 文本数据类型之一,则返回 true;如果是所有其他数据类型,则返回 false

错误:

如果出现以下情况,则会引发 JavaScript 错误:

  • 尚未执行 Statement

  • 不存在具有指定索引的列。

备注

此 API 提供了几种用于确定列数据类型的方法。第一种方法在上面有详细描述。其余方法具有相同的参数和错误;唯一的区别是返回值。

isColumnArray(colIdx)
返回:

如果列数据类型是 ARRAY (对于半结构化数据),则返回 true;如果是所有其他数据类型,则返回 false

isColumnBinary(colIdx)
返回:

如果列数据类型是 BINARY 或 true,则返回 VARBINARY;如果是所有其他数据类型,则返回 false

isColumnBoolean(colIdx)
返回:

如果列数据类型是 BOOLEAN,则返回 true;如果是所有其他数据类型,则返回 false

isColumnDate(colIdx)
返回:

如果列数据类型是 DATE,则返回 true;如果是所有其他数据类型,则返回 false

isColumnNumber(colIdx)
返回:

如果列数据类型是 SQL 数值类型之一(NUMBER、NUMERIC、DECIMAL、INT、INTEGER、 BIGINT、SMALLINT、TINYINT、BYTEINT、FLOAT、FLOAT4、FLOAT8、DOUBLE、DOUBLE PRECISION 或 REAL),则返回 true;如果是所有其他数据类型,则返回 false

isColumnObject(colIdx)
返回:

如果列数据类型是 OBJECT (对于半结构化数据),则返回 true;如果是所有其他数据类型,则返回 false

isColumnTime(colIdx)
返回:

如果列数据类型是 TIME 或 DATETIME,则返回 true;如果是所有其他数据类型,则返回 false

isColumnTimestamp(colIdx)
返回:

如果列数据类型是 SQL 时间戳类型之一(TIMESTAMP、TIMESTAMP_LTZ、TIMESTAMP_NTZ 或 TIMESTAMP_TZ),则返回 true;如果是所有其他数据类型(包括其他日期和时间数据类型,即 DATE、TIME 或 DATETIME),则返回 false

isColumnVariant(colIdx)
返回:

如果列数据类型是 VARIANT (对于半结构化数据),则返回 true;如果是所有其他数据类型,则返回 false

对象:ResultSet

此对象包含查询返回的结果。结果被视为一个含有零行或多行的集合,每行包含一列或多列。这里使用的术语“集合”并非数学意义上的集合。在数学中,集合是无序的,而 ResultSet 是有序的。

ResultSet 在某些方面类似于 SQL 游标的概念。例如,可以在 ResultSet 中一次查看一行,就像在游标中一次查看一行一样。

通常,在检索 ResultSet 后,通过重复以下操作来遍历它:

  • 调用 next() 以获取下一行。

  • 通过调用 getColumnValue() 等方法从当前行检索数据。

如果您对 ResultSet 中的数据了解不够(例如,不知道每列的数据类型),则可以调用其他方法来获取有关数据的信息。

ResultSet 对象的某些方法与 Statement 对象的方法类似。 例如,这两个对象都具有 getColumnSqlType(colIdx) 方法。

常量

无。

方法

getColumnCount()

此方法返回此 ResultSet 中的列数。

参数:

无。

返回:

数字类型的值,该值指示列数。

错误:

无。

getColumnSqlType(colIdx|colName)

此方法返回指定列的 SQL 数据类型。

参数:

列的索引号(从 1 而不是 0 开始)或列的名称。(此方法会被重载以接受不同的数据类型作为参数。)

列名应全部大写,除非在创建表时在列名中使用了双引号(即保留列名的大小写)。

返回:

列的 SQL 数据类型。

错误:

如果出现以下情况,则会引发 JavaScript 错误:

  • ResultSet 为空或尚未调用 next()

  • 不存在具有指定索引或名称的列。

getColumnValue(colIdx|colName)

此方法返回当前行(即 next() 最近检索到的行)中的列值。

参数:

列的索引号(从 1 而不是 0 开始)或列的名称。(此方法会被重载以接受不同的数据类型作为参数。)

列名应全部大写,除非在创建表时在列名中使用了双引号(即保留列名的大小写)。

返回:

指定列的值。

错误:

如果出现以下情况,则会引发 JavaScript 错误:

  • ResultSet 为空或尚未调用 next()

  • 不存在具有指定索引或名称的列。

示例:

将数据库中的行转换为 JavaScript 数组:

var valueArray = [];
// For each row...
while (myResultSet.next())  {
    // Append each column of the current row...
    valueArray.push(myResultSet.getColumnValue('MY_COLUMN_NAME1'));
    valueArray.push(myResultSet.getColumnValue('MY_COLUMN_NAME2'));
    ...
    // Do something with the row of data that we retrieved.
    f(valueArray);
    // Reset the array before getting the next row.
    valueArray = [];
    }
Copy

此外,列的值可以作为 ResultSet 对象(例如 myResultSet.MY_COLUMN_NAME)的属性来访问。

var valueArray = [];
// For each row...
while (myResultSet.next())  {
    // Append each column of the current row...
    valueArray.push(myResultSet.MY_COLUMN_NAME1);
    valueArray.push(myResultSet.MY_COLUMN_NAME2);
    ...
    // Do something with the row of data that we retrieved.
    f(valueArray);
    // Reset the array before getting the next row.
    valueArray = [];
    }
Copy

备注

请注意,除非在 CREATE TABLE 语句中用双引号分隔列名,否则 JavaScript 代码中的列名应全部大写。

getColumnValueAsString(colIdx|colName)

此方法以字符串形式返回列的值,这在无论表中的原始数据类型如何都需要列值时非常有用。

此方法返回字符串值,这是它唯一与 getColumnValue() 方法不同的地方。

有关更多详细信息,请参阅 getColumnValue()

getNumRowsAffected()

此方法返回受生成了此 ResultSet 的语句影响(例如插入/更新/删除)的行数。

如果应用了多种类型的变更(例如,MERGE 操作插入了一些行并更新了另一些行),则该数字是受所有变更影响的总行数。

参数:

无。

返回:

数字类型的值,该值指示受影响的行数。

错误:

无。

getQueryId()

此方法返回最近执行的查询的 UUID。

参数:

无。

返回:

包含 UUID 的字符串,即查询 ID。

示例:
var queryId = resultSet.getQueryId();
Copy
getRowCount()

此方法返回此 ResultSet 中的行数。(这是总行数,而不是尚未使用的行数。)

参数:

无。

返回:

数字类型的值,该值指示行数。

错误:

无。

next()

此方法获取 ResultSet 中的下一行,并使其可供访问。

此方法不返回新的数据行,而是使行变得可用,以便您可以调用 ResultSet.getColumnValue() 之类的方法来检索数据。

请注意,必须调用 next() 来获取结果集中的每一行,:emph:`包括`第一行。

参数:

无。

返回:

如果此方法检索了一行,则返回 true;如果没有更多行要检索,则返回 false

因此,可以遍历 ResultSet,直至 next() 返回 false。

错误:

无。

对象:SfDate

JavaScript 没有与 Snowflake SQL 数据类型 TIMESTAMP_LTZ、TIMESTAMP_NTZ 和 TIMESTAMP_TZ 对应的原生数据类型。如果从数据库中检索 TIMESTAMP 类型的值,并想将其存储为 JavaScript 变量(例如,将值从 ResultSet 复制到 JavaScript 变量),请使用 Snowflake 定义的 JavaScript 数据类型 SfDateSfDate`(“SnowFlake 日期”)数据类型是 JavaScript 日期数据类型的扩展。:code:`SfDate 具有额外的方法,如下所述。

常量

无。

方法

除非另有说明,否则以下示例假定时区为 UTC 时区。

getEpochSeconds()

此方法返回自“Unix 时间戳”(1970 年 1 月 1 日午夜)开始以来的秒数。

参数:

无。

返回:

1970 年 1 月 1 日午夜与变量中存储的时间戳之间的秒数。

示例:

创建存储过程:

CREATE OR REPLACE PROCEDURE test_get_epoch_seconds(TSV VARCHAR)
    RETURNS FLOAT
    LANGUAGE JAVASCRIPT
    AS
    $$
    var sql_command = "SELECT '" + TSV + "'::TIMESTAMP_NTZ;";
    var stmt = snowflake.createStatement( {sqlText: sql_command} );
    var resultSet = stmt.execute();
    resultSet.next();
    var my_sfDate = resultSet.getColumnValue(1);
    return my_sfDate.getEpochSeconds();
    $$
    ;
Copy

向过程传递不同的时间戳,并检索每个时间戳自 Unix 时间戳以来的秒数。

CALL test_get_epoch_seconds('1970-01-01 00:00:00.000000000');
+------------------------+
| TEST_GET_EPOCH_SECONDS |
|------------------------|
|                      0 |
+------------------------+
Copy
CALL test_get_epoch_seconds('1970-01-01 00:00:01.987654321');
+------------------------+
| TEST_GET_EPOCH_SECONDS |
|------------------------|
|                      1 |
+------------------------+
Copy
CALL test_get_epoch_seconds('1971-01-01 00:00:00');
+------------------------+
| TEST_GET_EPOCH_SECONDS |
|------------------------|
|               31536000 |
+------------------------+
Copy
getNanoSeconds()

此方法返回对象的纳秒字段的值。请注意,这只是小数秒,而不是自 Unix 时间戳开始以来的纳秒数。因此,该值始终介于 0 和 999999999 之间。

参数:

无。

返回:

纳秒数。

示例:

创建存储过程:

CREATE OR REPLACE PROCEDURE test_get_nano_seconds2(TSV VARCHAR)
    RETURNS FLOAT
    LANGUAGE JAVASCRIPT
    AS
    $$
    var sql_command = "SELECT '" + TSV + "'::TIMESTAMP_NTZ;";
    var stmt = snowflake.createStatement( {sqlText: sql_command} );
    var resultSet = stmt.execute();
    resultSet.next();
    var my_sfDate = resultSet.getColumnValue(1);
    return my_sfDate.getNanoSeconds();
    $$
    ;
-- Should be 0 nanoseconds.
-- (> SNIPPET_TAG=query_03_01
CALL test_get_nano_seconds2('1970-01-01 00:00:00.000000000');
Copy

向过程传递不同的时间戳,并从每个时间戳中检索纳秒数。

CALL test_get_nano_seconds2('1970-01-01 00:00:00.000000000');
+------------------------+
| TEST_GET_NANO_SECONDS2 |
|------------------------|
|                      0 |
+------------------------+
Copy
CALL test_get_nano_seconds2('1970-01-01 00:00:01.987654321');
+------------------------+
| TEST_GET_NANO_SECONDS2 |
|------------------------|
|              987654321 |
+------------------------+
Copy
CALL test_get_nano_seconds2('1971-01-01 00:00:00.000123456');
+------------------------+
| TEST_GET_NANO_SECONDS2 |
|------------------------|
|                 123456 |
+------------------------+
Copy
getScale()

此方法返回数据类型的精度,即小数点后的位数。例如,TIMESTAMP_NTZ(3) 的精度为 3(毫秒)。TIMESTAMP_NTZ(0) 的精度为 0(无小数秒)。TIMSTAMP_NTZ 的精度为 9(纳秒)。

最小值为 0。最大值为 9(精度为 1 纳秒)。 默认精度为 9。

参数:

无。

返回:

小数位后的位数(小数秒字段中的位数)。

示例:

创建存储过程:

CREATE OR REPLACE PROCEDURE test_get_scale(TSV VARCHAR, SCALE VARCHAR)
    RETURNS FLOAT
    LANGUAGE JAVASCRIPT
    AS
    $$
    var sql_command = "SELECT '" + TSV + "'::TIMESTAMP_NTZ(" + SCALE + ");";
    var stmt = snowflake.createStatement( {sqlText: sql_command} );
    var resultSet = stmt.execute();
    resultSet.next();
    var my_sfDate = resultSet.getColumnValue(1);
    return my_sfDate.getScale();
    $$
    ;

-- Should be 0.
-- (> SNIPPET_TAG=query_04_01
CALL test_get_scale('1970-01-01 00:00:00', '0');
Copy

在此示例中,时间戳定义为 TIMESTAMP_NTZ(0),因此精度为 0。

CALL test_get_scale('1970-01-01 00:00:00', '0');
+----------------+
| TEST_GET_SCALE |
|----------------|
|              0 |
+----------------+
Copy

在此示例中,时间戳定义为 TIMESTAMP_NTZ(2),因此精度为 2。

CALL test_get_scale('1970-01-01 00:00:01.123', '2');
+----------------+
| TEST_GET_SCALE |
|----------------|
|              2 |
+----------------+
Copy

在此示例中,时间戳定义为 TIMESTAMP_NTZ,因此精度为 9(这是默认值)。

CALL test_get_scale('1971-01-01 00:00:00.000123456', '9');
+----------------+
| TEST_GET_SCALE |
|----------------|
|              9 |
+----------------+
Copy
getTimezone()

此方法以早于或晚于 UTC 多少分钟的形式返回时区。

参数:

无。

返回:

以早于或晚于 UTC 多少分钟来表示的时区。

示例:

创建存储过程:

CREATE OR REPLACE PROCEDURE test_get_Timezone(TSV VARCHAR)
    RETURNS FLOAT
    LANGUAGE JAVASCRIPT
    AS
    $$
    var sql_command = "SELECT '" + TSV + "'::TIMESTAMP_TZ;";
    var stmt = snowflake.createStatement( {sqlText: sql_command} );
    var resultSet = stmt.execute();
    resultSet.next();
    var my_sfDate = resultSet.getColumnValue(1);
    return my_sfDate.getTimezone();
    $$
    ;
Copy

在此示例中,时区比 UTC 晚 8 小时(480 分钟)。

CALL test_get_timezone('1970-01-01 00:00:01-08:00');
+-------------------+
| TEST_GET_TIMEZONE |
|-------------------|
|              -480 |
+-------------------+
Copy

在此示例中,时区比 UTC 早 11 小时(660 分钟)。

CALL test_get_timezone('1971-01-01 00:00:00.000123456+11:00');
+-------------------+
| TEST_GET_TIMEZONE |
|-------------------|
|               660 |
+-------------------+
Copy
toString()
参数:

无。

返回:

此方法返回时间戳的字符串表示形式。

示例:

下面显示了创建 SfDate 并调用其 toString 方法的简单示例:

CREATE OR REPLACE PROCEDURE test_toString(TSV VARCHAR)
    RETURNS VARIANT
    LANGUAGE JAVASCRIPT
    AS
    $$
    var sql_command = "SELECT '" + TSV + "'::TIMESTAMP_TZ;";
    var stmt = snowflake.createStatement( {sqlText: sql_command} );
    var resultSet = stmt.execute();
    resultSet.next();
    var my_sfDate = resultSet.getColumnValue(1);
    return my_sfDate.toString();
    $$
    ;
Copy
CALL test_toString('1970-01-02 03:04:05');
+------------------------------------------------------------------+
| TEST_TOSTRING                                                    |
|------------------------------------------------------------------|
| "Fri Jan 02 1970 03:04:05 GMT+0000 (Coordinated Universal Time)" |
+------------------------------------------------------------------+
Copy
语言: 中文