3

如何访问传递给 pl/tcl 函数的数组?参数不应该变成一个列表吗?在下面的代码中,无论输入是什么,它都会打印“1”,例如“select foobar(array[1,2,3]);”

我对此感兴趣的是构建自定义聚合,这些聚合使用数组来保持调用之间的状态。

编辑:

这是一个新功能:

create function foobar(int[]) returns int[] as $$
    elog INFO "$1, [llength $1]"
    return $1
$$ language pltcl;

这是调用和输出,它表明将数组输入和输出函数是有效的:

postgis20=# select (array[1,2,3])[1];
 array
-------
     1
(1 row)

postgis20=# select foobar(array[1,2,3]);
INFO:  {1,2,3}, 1
 foobar
---------
 {1,2,3}
(1 row)

postgis20=# select (foobar(array[1,2,3]))[1];
INFO:  {1,2,3}, 1
 foobar
--------
      1
(1 row)

谢谢!

4

1 回答 1

1

The documentation is a little gnostic, but it includes this:

The argument values supplied to a PL/Tcl function's code are simply the input arguments converted to text form (just as if they had been displayed by a SELECT statement). Conversely, the return command will accept any string that is acceptable input format for the function's declared return type. So, within the PL/Tcl function, all values are just text strings.

So the array value from Tcl's PoV is going to be {1,2,3} or something like that. Everything else is converting between that serialization and Tcl's lists. (All this will also work just fine for other types of numeric array, but will be dangerous if you do arrays of characters or strings, as the delimiter characters can appear within. Of course, in that case I'd also think you were doing something a bit wrong in the first place…)

Handling PostgreSQL int[] arrays

To extract the integers from such a thing, we'd do:

set tclListOfValues [split [string trim $1 "{}"] ","]

After that, you can use llength and lindex and foreach and so on. The reverse operation, converting a Tcl list (assumed to be of integers) to an int[] for returning:

return \{[join $tclListOfValues ","]\}

Handling nested PostgreSQL arrays

Things will get a lot more awkward if you need to access nested arrays. The issue is that it's a two-level split, and that's best done by applying some clever magic with string map before the outer split:

set listOfLists {}
foreach innerArray [split [string map {"\},\{" "\u0000"} [string trim $1 "{}"]] "\u0000"] {
    lappend listOfLists [split $innerArray ","]
}

In Tcl 8.6, you can do this slightly more neatly with lmap like this (conceptually a one-liner, but broken up for clarity):

set listOfLists [lmap innerArray \
        [split [string map {"\},\{" "\u0000"} [string trim $1 "{}"]] "\u0000"] {
    split $innerArray ","
}]

In the reverse direction:

set resultAccumulator {}
foreach innerList $listOfLists {
    lappend resultAccumulator [join $innerList ","]
}
return \{[join $resultAccumulator "\},\{"]\}

Or in Tcl 8.6 style with lmap again:

return \{[join [lmap innerList $listOfLists {join $innerList ","}] "\},\{"]\}
于 2013-07-30T08:52:27.117 回答