Thread
Commits
-
Don't specify number of dimensions in cases where we don't know it.
- 8d5573b92e66 17.0 landed
-
Improve readability and error detection of array_in().
- 83472de606db 17.0 landed
-
Add trailing commas to enum definitions
- 611806cd726f 17.0 cited
-
Cleaning up array_in()
Tom Lane <tgl@sss.pgh.pa.us> — 2023-05-02T15:41:27Z
This is in response to Alexander's observation at [1], but I'm starting a fresh thread to keep this patch separate from the plperl fixes in the cfbot's eyes. Alexander Lakhin <exclusion@gmail.com> writes: > I continue watching the array handling bugs dancing Sirtaki too. Now it's > another asymmetry: > select '{{1},{{2}}}'::int[]; > {{{1}},{{2}}} > but: > select '{{{1}},{2}}'::int[]; > {} Bleah. Both of those should be rejected, for sure, but it's the same situation as in the PLs: we weren't doing anything to enforce that all the scalar elements appear at the same nesting depth. I spent some time examining array_in(), and was pretty disheartened by what a mess it is. It looks like back in the dim mists of the Berkeley era, there was an intentional attempt to allow non-rectangular array input, with the missing elements automatically filled out as NULLs. Since that was undocumented, we concluded it was a bug and plastered on some code to check for rectangularity of the input. I don't quibble with enforcing rectangularity, but the underlying logic should have been simplified while we were at it. The element-counting logic was basically magic (why is it okay to increment temp[ndim - 1] when the current nest_level might be different from that?) and the extra layers of checks didn't make it any more intelligible. Plus, ReadArrayStr was expending far more cycles than it needs to given the assumption of rectangularity. So, here's a rewrite. Although I view this as a bug fix, AFAICT the only effects are to accept input that should be rejected. So again I don't advocate back-patching. But should we sneak it into v16, or wait for v17? regards, tom lane [1] https://www.postgresql.org/message-id/9cd163da-d096-7e9e-28f6-f3620962a660%40gmail.com -
Re: Cleaning up array_in()
Nathan Bossart <nathandbossart@gmail.com> — 2023-05-08T23:40:54Z
On Tue, May 02, 2023 at 11:41:27AM -0400, Tom Lane wrote: > It looks like back in the dim mists of the > Berkeley era, there was an intentional attempt to allow > non-rectangular array input, with the missing elements automatically > filled out as NULLs. Since that was undocumented, we concluded it was > a bug and plastered on some code to check for rectangularity of the > input. Interesting. > Although I view this as a bug fix, AFAICT the only effects are to > accept input that should be rejected. So again I don't advocate > back-patching. But should we sneak it into v16, or wait for v17? I think it'd be okay to sneak it into v16, given it is technically a bug fix. > (This leaves ArrayGetOffset0() unused, but I'm unsure whether to > remove that.) Why's that? Do you think it is likely to be used again in the future? Otherwise, 0001 LGTM. I haven't had a chance to look at 0002 closely yet. -- Nathan Bossart Amazon Web Services: https://aws.amazon.com
-
Re: Cleaning up array_in()
Alexander Law <exclusion@gmail.com> — 2023-05-09T03:00:00Z
02.05.2023 18:41, Tom Lane wrote: > So, here's a rewrite. > > Although I view this as a bug fix, AFAICT the only effects are to > accept input that should be rejected. So again I don't advocate > back-patching. But should we sneak it into v16, or wait for v17? I've tested the patch from a user perspective and found no interesting cases that were valid before, but not accepted with the patch (or vice versa): The only thing that confused me, is the error message (it's not new, too): select '{{{{{{{{{{1}}}}}}}}}}'::int[]; or even: select '{{{{{{{{{{'::int[]; ERROR: number of array dimensions (7) exceeds the maximum allowed (6) Maybe it could be reworded like that?: too many opening braces defining dimensions (maximum dimensions allowed: 6) Beside that, I would like to note the following error text changes (all of these are feasible, I think): select '{{1},{{'::int[]; Before: ERROR: malformed array literal: "{{1},{{" LINE 1: select '{{1},{{'::int[]; ^ DETAIL: Unexpected end of input. After: ERROR: malformed array literal: "{{1},{{" LINE 1: select '{{1},{{'::int[]; ^ DETAIL: Multidimensional arrays must have sub-arrays with matching dimensions. --- select '{{1},{{{{{{'::int[]; Before: ERROR: number of array dimensions (7) exceeds the maximum allowed (6) After: ERROR: malformed array literal: "{{1},{{{{{{" LINE 1: select '{{1},{{{{{{'::int[]; ^ DETAIL: Multidimensional arrays must have sub-arrays with matching dimensions. --- select '{{1},{}}}'::int[]; Before: ERROR: malformed array literal: "{{1},{}}}" LINE 1: select '{{1},{}}}'::int[]; ^ DETAIL: Unexpected "}" character. After: ERROR: malformed array literal: "{{1},{}}}" LINE 1: select '{{1},{}}}'::int[]; ^ DETAIL: Multidimensional arrays must have sub-arrays with matching dimensions. --- select '{{}}}'::int[]; Before: ERROR: malformed array literal: "{{}}}" LINE 1: select '{{}}}'::int[]; ^ DETAIL: Unexpected "}" character. After: ERROR: malformed array literal: "{{}}}" LINE 1: select '{{}}}'::int[]; ^ DETAIL: Junk after closing right brace. Best regards, Alexander -
Re: Cleaning up array_in()
Tom Lane <tgl@sss.pgh.pa.us> — 2023-05-09T03:06:47Z
Alexander Lakhin <exclusion@gmail.com> writes: > The only thing that confused me, is the error message (it's not new, too): > select '{{{{{{{{{{1}}}}}}}}}}'::int[]; > or even: > select '{{{{{{{{{{'::int[]; > ERROR: number of array dimensions (7) exceeds the maximum allowed (6) Yeah, I didn't touch that, but it's pretty bogus because the first number will always be "7" even if you wrote more than 7 left braces, since the code errors out immediately upon finding that it's seen too many braces. The equivalent message in the PLs just says "number of array dimensions exceeds the maximum allowed (6)". I'm inclined to do likewise in array_in, but didn't touch it here. > Beside that, I would like to note the following error text changes > (all of these are feasible, I think): I'll look into whether we can improve those, unless you had a patch in mind already? regards, tom lane -
Re: Cleaning up array_in()
Alexander Law <exclusion@gmail.com> — 2023-05-09T11:00:01Z
09.05.2023 06:06, Tom Lane wrote: > Alexander Lakhin <exclusion@gmail.com> writes: >> The only thing that confused me, is the error message (it's not new, too): >> select '{{{{{{{{{{1}}}}}}}}}}'::int[]; >> or even: >> select '{{{{{{{{{{'::int[]; >> ERROR: number of array dimensions (7) exceeds the maximum allowed (6) > Yeah, I didn't touch that, but it's pretty bogus because the first > number will always be "7" even if you wrote more than 7 left braces, > since the code errors out immediately upon finding that it's seen > too many braces. > > The equivalent message in the PLs just says "number of array dimensions > exceeds the maximum allowed (6)". I'm inclined to do likewise in > array_in, but didn't touch it here. I think that, strictly speaking, we have no array dimensions in the string '{{{{{{{{{{'; there are only characters (braces) during the string parsing. While in the PLs we definitely deal with real arrays, which have dimensions. >> Beside that, I would like to note the following error text changes >> (all of these are feasible, I think): > I'll look into whether we can improve those, unless you had a patch > in mind already? Those messages looked more or less correct to me, I just wanted to note how they are changing (and haven't highlighted messages, that are not), but if you see here room for improvement, please look into it (I have no good formulations yet). Best regards, Alexander -
Re: Cleaning up array_in()
Nikhil Benesch <nikhil.benesch@gmail.com> — 2023-06-05T01:48:38Z
I took a look at 0002 because I attempted a similar but more surgical fix in [0]. I spotted a few opportunities for further reducing state tracked by `ArrayCount`. You may not find all of these suggestions to be worthwhile. 1) `in_quotes` appears to be wholly redundant with `parse_state == ARRAY_QUOTED_ELEM_STARTED`. 2) The `empty_array` special case does not seem to be important to ArrayCount's callers, which don't even special case `ndims == 0` but rather `ArrayGetNItemsSafe(..) == 0`. Perhaps this is a philosophical question as to whether `ArrayCount('{{}, {}}')` should return (ndims=2, dims=[2, 0]) or (ndims=0). Obviously someone needs to do that normalization, but `ArrayCount` could leave that normalization to `ReadArrayStr`. 3) `eoArray` could be replaced with a new `ArrayParseState` of `ARRAY_END`. Just a matter of taste, but "end of array" feels like a parser state to me. I also have a sense that `ndims_frozen` made the distinction between `ARRAY_ELEM_DELIMITED` and `ARRAY_LEVEL_DELIMITED` unnecessary, and the two states could be merged into a single `ARRAY_DELIMITED` state, but I've not pulled on this thread hard enough to say so confidently. Thanks for doing the serious overhaul. As you say, the element counting logic is much easier to follow now. I'm much more confident that your patch is correct than mine. Cheers, Nikhil [0]: https://www.postgresql.org/message-id/CAPWqQZRHsFuvWJj%3DczXuKEB03LF4ctPpDE1k3CoexweEFicBKQ%40mail.gmail.com On Tue, May 9, 2023 at 7:00 AM Alexander Lakhin <exclusion@gmail.com> wrote: > > 09.05.2023 06:06, Tom Lane wrote: > > Alexander Lakhin <exclusion@gmail.com> writes: > >> The only thing that confused me, is the error message (it's not new, too): > >> select '{{{{{{{{{{1}}}}}}}}}}'::int[]; > >> or even: > >> select '{{{{{{{{{{'::int[]; > >> ERROR: number of array dimensions (7) exceeds the maximum allowed (6) > > Yeah, I didn't touch that, but it's pretty bogus because the first > > number will always be "7" even if you wrote more than 7 left braces, > > since the code errors out immediately upon finding that it's seen > > too many braces. > > > > The equivalent message in the PLs just says "number of array dimensions > > exceeds the maximum allowed (6)". I'm inclined to do likewise in > > array_in, but didn't touch it here. > > I think that, strictly speaking, we have no array dimensions in the string > '{{{{{{{{{{'; there are only characters (braces) during the string parsing. > While in the PLs we definitely deal with real arrays, which have dimensions. > > >> Beside that, I would like to note the following error text changes > >> (all of these are feasible, I think): > > I'll look into whether we can improve those, unless you had a patch > > in mind already? > > Those messages looked more or less correct to me, I just wanted to note how they are > changing (and haven't highlighted messages, that are not), but if you see here room > for improvement, please look into it (I have no good formulations yet). > > Best regards, > Alexander > > -
Re: Cleaning up array_in()
Tom Lane <tgl@sss.pgh.pa.us> — 2023-06-05T02:38:28Z
Nikhil Benesch <nikhil.benesch@gmail.com> writes: > I took a look at 0002 because I attempted a similar but more surgical > fix in [0]. > I spotted a few opportunities for further reducing state tracked by > `ArrayCount`. Wow, thanks for looking! I've not run these suggestions to ground (and won't have time for a few days), but they sound very good. regards, tom lane
-
Re: Cleaning up array_in()
jian he <jian.universality@gmail.com> — 2023-07-03T04:16:22Z
On Mon, Jun 5, 2023 at 9:48 AM Nikhil Benesch <nikhil.benesch@gmail.com> wrote: > > I took a look at 0002 because I attempted a similar but more surgical > fix in [0]. > > I spotted a few opportunities for further reducing state tracked by > `ArrayCount`. You may not find all of these suggestions to be > worthwhile. I pull ArrayCount into a separate C function, regress test (manually) based on patch regress test. > 1) `in_quotes` appears to be wholly redundant with `parse_state == > ARRAY_QUOTED_ELEM_STARTED`. removing it works as expected. > 3) `eoArray` could be replaced with a new `ArrayParseState` of > `ARRAY_END`. Just a matter of taste, but "end of array" feels like a > parser state to me. works. (reduce one variable.) > I also have a sense that `ndims_frozen` made the distinction between > `ARRAY_ELEM_DELIMITED` and `ARRAY_LEVEL_DELIMITED` unnecessary, and > the two states could be merged into a single `ARRAY_DELIMITED` state, > but I've not pulled on this thread hard enough to say so confidently. merging these states into one work as expected.
-
Re: Cleaning up array_in()
jian he <jian.universality@gmail.com> — 2023-07-03T11:52:23Z
based on Nikhil Benesch idea. The attached diff is based on v1-0002-Rewrite-ArrayCount-to-make-dimensionality-checks.patch. diff compare v1-0002: select '{{1,{2}},{2,3}}'::text[]; ERROR: malformed array literal: "{{1,{2}},{2,3}}" LINE 1: select '{{1,{2}},{2,3}}'::text[]; ^ -DETAIL: Unexpected "{" character. +DETAIL: Multidimensional arrays must have sub-arrays with matching dimensions. ---------------------------------------------------- select E'{{1,2},\\{2,3}}'::text[]; ERROR: malformed array literal: "{{1,2},\{2,3}}" LINE 1: select E'{{1,2},\\{2,3}}'::text[]; ^ -DETAIL: Unexpected "\" character. +DETAIL: Multidimensional arrays must have sub-arrays with matching dimensions. ------- new errors details kind of make sense. -
Re: Cleaning up array_in()
Tom Lane <tgl@sss.pgh.pa.us> — 2023-07-04T18:33:14Z
Nikhil Benesch <nikhil.benesch@gmail.com> writes: > I spotted a few opportunities for further reducing state tracked by > `ArrayCount`. You may not find all of these suggestions to be > worthwhile. I found some time today to look at these points. > 1) `in_quotes` appears to be wholly redundant with `parse_state == > ARRAY_QUOTED_ELEM_STARTED`. I agree that it is redundant, but I'm disinclined to remove it because the in_quotes logic matches that in ReadArrayStr. I think it's better to keep those two functions in sync. The parse_state represents an independent set of checks that need not be repeated by ReadArrayStr, but both functions have to track quoting. The same for eoArray. > 2) The `empty_array` special case does not seem to be important to > ArrayCount's callers, which don't even special case `ndims == 0` but > rather `ArrayGetNItemsSafe(..) == 0`. Perhaps this is a philosophical > question as to whether `ArrayCount('{{}, {}}')` should return > (ndims=2, dims=[2, 0]) or (ndims=0). Obviously someone needs to do > that normalization, but `ArrayCount` could leave that normalization to > `ReadArrayStr`. This idea I do like. While looking at the callers, I also noticed that it's impossible currently to write an empty array with explicit specification of bounds. It seems to me that you ought to be able to write, say, SELECT '[1:0]={}'::int[]; but up to now you got "upper bound cannot be less than lower bound"; and if you somehow got past that, you'd get "Specified array dimensions do not match array contents." because of ArrayCount's premature optimization of "one-dimensional array with length zero" to "zero-dimensional array". We can fix that by doing what you said and adjusting the initial bounds restriction to be "upper bound cannot be less than lower bound minus one". > I also have a sense that `ndims_frozen` made the distinction between > `ARRAY_ELEM_DELIMITED` and `ARRAY_LEVEL_DELIMITED` unnecessary, and > the two states could be merged into a single `ARRAY_DELIMITED` state, > but I've not pulled on this thread hard enough to say so confidently. I looked at jian he's implementation of that and was not impressed: I do not think the logic gets any clearer, and it seems to me that this makes a substantial dent in ArrayCount's ability to detect syntax errors. The fact that one of the test case error messages got better seems pretty accidental to me. We can get the same result in a more purposeful way by giving a different error message for ARRAY_ELEM_DELIMITED. So I end up with the attached. I went ahead and dropped ArrayGetOffset0() as part of 0001, and I split 0002 into two patches where the new 0002 avoids re-indenting any existing code in order to ease review, and then 0003 is just a mechanical application of pgindent. I still didn't do anything about "number of array dimensions (7) exceeds the maximum allowed (6)". There are quite a few instances of that wording, not only array_in's, and I'm not sure whether to change the rest. In any case that looks like something that could be addressed separately. The other error message wording changes here seem to me to be okay. regards, tom lane -
Re: Cleaning up array_in()
Tom Lane <tgl@sss.pgh.pa.us> — 2023-07-08T16:08:52Z
I wrote: > So I end up with the attached. I went ahead and dropped > ArrayGetOffset0() as part of 0001, and I split 0002 into two patches > where the new 0002 avoids re-indenting any existing code in order > to ease review, and then 0003 is just a mechanical application > of pgindent. That got sideswiped by ae6d06f09, so here's a trivial rebase to pacify the cfbot. regards, tom lane #text/x-diff; name="v3-0001-Simplify-and-speed-up-ReadArrayStr.patch" [v3-0001-Simplify-and-speed-up-ReadArrayStr.patch] /home/tgl/pgsql/v3-0001-Simplify-and-speed-up-ReadArrayStr.patch #text/x-diff; name="v3-0002-Rewrite-ArrayCount-to-make-dimensionality-checks-.patch" [v3-0002-Rewrite-ArrayCount-to-make-dimensionality-checks-.patch] /home/tgl/pgsql/v3-0002-Rewrite-ArrayCount-to-make-dimensionality-checks-.patch #text/x-diff; name="v3-0003-Re-indent-ArrayCount.patch" [v3-0003-Re-indent-ArrayCount.patch] /home/tgl/pgsql/v3-0003-Re-indent-ArrayCount.patch
-
Re: Cleaning up array_in()
Tom Lane <tgl@sss.pgh.pa.us> — 2023-07-08T19:34:51Z
I wrote: > That got sideswiped by ae6d06f09, so here's a trivial rebase to > pacify the cfbot. Sigh, this time with patch. regards, tom lane
-
Re: Cleaning up array_in()
Heikki Linnakangas <hlinnaka@iki.fi> — 2023-07-08T19:38:31Z
On 08/07/2023 19:08, Tom Lane wrote: > I wrote: >> So I end up with the attached. I went ahead and dropped >> ArrayGetOffset0() as part of 0001, and I split 0002 into two patches >> where the new 0002 avoids re-indenting any existing code in order >> to ease review, and then 0003 is just a mechanical application >> of pgindent. > > That got sideswiped by ae6d06f09, so here's a trivial rebase to > pacify the cfbot. > > #text/x-diff; name="v3-0001-Simplify-and-speed-up-ReadArrayStr.patch" [v3-0001-Simplify-and-speed-up-ReadArrayStr.patch] /home/tgl/pgsql/v3-0001-Simplify-and-speed-up-ReadArrayStr.patch > #text/x-diff; name="v3-0002-Rewrite-ArrayCount-to-make-dimensionality-checks-.patch" [v3-0002-Rewrite-ArrayCount-to-make-dimensionality-checks-.patch] /home/tgl/pgsql/v3-0002-Rewrite-ArrayCount-to-make-dimensionality-checks-.patch > #text/x-diff; name="v3-0003-Re-indent-ArrayCount.patch" [v3-0003-Re-indent-ArrayCount.patch] /home/tgl/pgsql/v3-0003-Re-indent-ArrayCount.patch Something's wrong with your attachments. Hmm, I wonder if ae6d06f09 had a negative performance impact. In an unquoted array element, scanner_isspace() function is called for every character, so it might be worth inlining. On the patches: They are a clear improvement, thanks for that. That said, I still find the logic very hard to follow, and there are some obvious performance optimizations that could be made. ArrayCount() interprets low-level quoting and escaping, and tracks the dimensions at the same time. The state machine is pretty complicated. And when you've finally finished reading and grokking that function, you see that ReadArrayStr() repeats most of the same logic. Ugh. I spent some time today refactoring it for readability and speed. I introduced a separate helper function to tokenize the input. It deals with whitespace, escapes, and backslashes. Then I merged ArrayCount() and ReadArrayStr() into one function that parses the elements and determines the dimensions in one pass. That speeds up parsing large arrays. With the tokenizer function, the logic in ReadArrayStr() is still quite readable, even though it's now checking the dimensions at the same time. I also noticed that we used atoi() to parse the integers in the dimensions, which doesn't do much error checking. Some funny cases were accepted because of that, for example: postgres=# select '[1+-+-+-+-+-+:2]={foo,bar}'::text[]; text ----------- {foo,bar} (1 row) I tightened that up in the passing. Attached are your patches, rebased to fix the conflicts with ae6d06f09 like you intended. On top of that, my patches. My patches need more testing, benchmarking, and review, so if we want to sneak something into v16, better go with just your patches. If we're tightening up the accepted inputs, maybe fix that atoi() sloppiness, though. -- Heikki Linnakangas Neon (https://neon.tech) -
Re: Cleaning up array_in()
Tom Lane <tgl@sss.pgh.pa.us> — 2023-07-08T19:49:31Z
Heikki Linnakangas <hlinnaka@iki.fi> writes: > On 08/07/2023 19:08, Tom Lane wrote: >> That got sideswiped by ae6d06f09, so here's a trivial rebase to >> pacify the cfbot. > Something's wrong with your attachments. Yeah, I forgot to run mhbuild :-( > I spent some time today refactoring it for readability and speed. I > introduced a separate helper function to tokenize the input. It deals > with whitespace, escapes, and backslashes. Then I merged ArrayCount() > and ReadArrayStr() into one function that parses the elements and > determines the dimensions in one pass. That speeds up parsing large > arrays. With the tokenizer function, the logic in ReadArrayStr() is > still quite readable, even though it's now checking the dimensions at > the same time. Oh, thanks for taking a look! > I also noticed that we used atoi() to parse the integers in the > dimensions, which doesn't do much error checking. Yup, I'd noticed that too but not gotten around to doing anything about it. I agree with nailing it down better as long as we're tightening things in this area. > Attached are your patches, rebased to fix the conflicts with ae6d06f09 > like you intended. On top of that, my patches. My patches need more > testing, benchmarking, and review, so if we want to sneak something into > v16, better go with just your patches. At this point I'm only proposing this for v17, so additional cleanup is welcome. BTW, what's your opinion of allowing "[1:0]={}" ? Although that was my proposal to begin with, I'm having second thoughts about it now. The main reason is that the input transformation would be lossy, eg "[1:0]={}" and "[101:100]={}" would give the same results, which seems a little ugly. Given the lack of field complaints, maybe we should leave that alone. regards, tom lane -
Re: Cleaning up array_in()
Heikki Linnakangas <hlinnaka@iki.fi> — 2023-07-08T20:03:47Z
On 08/07/2023 22:49, Tom Lane wrote: > BTW, what's your opinion of allowing "[1:0]={}" ? Although that was > my proposal to begin with, I'm having second thoughts about it now. > The main reason is that the input transformation would be lossy, > eg "[1:0]={}" and "[101:100]={}" would give the same results, which > seems a little ugly. Hmm, yeah, that would feel wrong if you did something like this: select ('[2:1]={}'::text[]) || '{x}'::text[]; and expected it to return '[2:2]={x}'. I guess we could allow "[1:0]={}" as a special case, but not "[101:100]={}", but that would be weird too. > Given the lack of field complaints, maybe we should leave that > alone. +1 to leave it alone. It's a little weird either way, so better to stay put. We can revisit it later if we want to, but I wouldn't want to go back and forth on it. -- Heikki Linnakangas Neon (https://neon.tech) -
Re: Cleaning up array_in()
jian he <jian.universality@gmail.com> — 2023-07-11T03:34:37Z
On Sun, Jul 9, 2023 at 3:38 AM Heikki Linnakangas <hlinnaka@iki.fi> wrote: > > On 08/07/2023 19:08, Tom Lane wrote: > > I wrote: > >> So I end up with the attached. I went ahead and dropped > >> ArrayGetOffset0() as part of 0001, and I split 0002 into two patches > >> where the new 0002 avoids re-indenting any existing code in order > >> to ease review, and then 0003 is just a mechanical application > >> of pgindent. > > > > That got sideswiped by ae6d06f09, so here's a trivial rebase to > > pacify the cfbot. > > > > #text/x-diff; name="v3-0001-Simplify-and-speed-up-ReadArrayStr.patch" [v3-0001-Simplify-and-speed-up-ReadArrayStr.patch] /home/tgl/pgsql/v3-0001-Simplify-and-speed-up-ReadArrayStr.patch > > #text/x-diff; name="v3-0002-Rewrite-ArrayCount-to-make-dimensionality-checks-.patch" [v3-0002-Rewrite-ArrayCount-to-make-dimensionality-checks-.patch] /home/tgl/pgsql/v3-0002-Rewrite-ArrayCount-to-make-dimensionality-checks-.patch > > #text/x-diff; name="v3-0003-Re-indent-ArrayCount.patch" [v3-0003-Re-indent-ArrayCount.patch] /home/tgl/pgsql/v3-0003-Re-indent-ArrayCount.patch > > Something's wrong with your attachments. > > Hmm, I wonder if ae6d06f09 had a negative performance impact. In an > unquoted array element, scanner_isspace() function is called for every > character, so it might be worth inlining. > > On the patches: They are a clear improvement, thanks for that. That > said, I still find the logic very hard to follow, and there are some > obvious performance optimizations that could be made. > > ArrayCount() interprets low-level quoting and escaping, and tracks the > dimensions at the same time. The state machine is pretty complicated. > And when you've finally finished reading and grokking that function, you > see that ReadArrayStr() repeats most of the same logic. Ugh. > > I spent some time today refactoring it for readability and speed. I > introduced a separate helper function to tokenize the input. It deals > with whitespace, escapes, and backslashes. Then I merged ArrayCount() > and ReadArrayStr() into one function that parses the elements and > determines the dimensions in one pass. That speeds up parsing large > arrays. With the tokenizer function, the logic in ReadArrayStr() is > still quite readable, even though it's now checking the dimensions at > the same time. > > I also noticed that we used atoi() to parse the integers in the > dimensions, which doesn't do much error checking. Some funny cases were > accepted because of that, for example: > > postgres=# select '[1+-+-+-+-+-+:2]={foo,bar}'::text[]; > text > ----------- > {foo,bar} > (1 row) > > I tightened that up in the passing. > > Attached are your patches, rebased to fix the conflicts with ae6d06f09 > like you intended. On top of that, my patches. My patches need more > testing, benchmarking, and review, so if we want to sneak something into > v16, better go with just your patches. If we're tightening up the > accepted inputs, maybe fix that atoi() sloppiness, though. > > -- > Heikki Linnakangas > Neon (https://neon.tech) your idea is so clear!!! all the Namings are way more descriptive. ArrayToken, personally something with "state", "type" will be more clear. > /* > * FIXME: Is this still required? I believe all the checks it performs are > * redundant with other checks in ReadArrayDimension() and ReadArrayStr() > */ > nitems_according_to_dims = ArrayGetNItemsSafe(ndim, dim, escontext); > if (nitems_according_to_dims < 0) > PG_RETURN_NULL(); > if (nitems != nitems_according_to_dims) > elog(ERROR, "mismatch nitems, %d vs %d", nitems, nitems_according_to_dims); > if (!ArrayCheckBoundsSafe(ndim, dim, lBound, escontext)) > PG_RETURN_NULL(); --first time run select '[0:3][0:2]={{1,2,3}, {4,5,6}, {7,8,9},{1,2,3}}'::int[]; INFO: 253 after ReadArrayDimensions dim: 4 3 71803430 21998 103381120 21998 ndim: 2 INFO: 770 after ReadArrayStr: dim: 4 3 71803430 21998 103381120 21998 nitems:12, ndim:2 --second time run. INFO: 253 after ReadArrayDimensions dim: 4 3 0 0 0 0 ndim: 2 INFO: 770 after ReadArrayStr: dim: 4 3 0 0 0 0 nitems:12, ndim:2 select '{{1,2,3}, {4,5,6}, {7,8,9},{1,2,3}}'::int[]; --every time run, the result is the same. INFO: 253 after ReadArrayDimensions dim: 0 0 0 0 0 0 ndim: 0 INFO: 770 after ReadArrayStr: dim: 4 3 -1 -1 -1 -1 nitems:12, ndim:2 I think the reason is that the dim int array didn't explicitly assign value when initializing it. > /* Now it's safe to compute ub + 1 */ > if (ub + 1 < lBound[ndim]) > ereturn(escontext, false, > (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), > errmsg("upper bound cannot be less than lower bound minus one"))); This part seems safe against cases like select '[-2147483649:-2147483648]={1,2}'::int[]; but I am not sure. If so, then ArrayCheckBoundsSafe is unnecessary. Another corner case successed: select '{1,}'::int[]; should fail. -
Re: Cleaning up array_in()
jian he <jian.universality@gmail.com> — 2023-08-07T05:35:03Z
hi. based on Heikki v3. I made some changes: array_in: dim[6] all initialize with -1, lBound[6] all initialize with 1. if ReadArrayDimensions called, then corresponding dimension lBound will replace the initialized default 1 value. ReadArrayStr, since array_in main function initialized dim array, dimensions_specified true or false, I don't need to initialize again, so I deleted that part. to solve corner cases like '{{1,},{1},}'::text[]. in ReadArrayStr main switch function, like other ArrayToken, first evaluate expect_delim then assign expect_delim. In ATOK_LEVEL_END. if non-empty array, closing bracket either precede with an element or another closing element. In both cases, the previous expect_delim should be true. in * FIXME: Is this still required? I believe all the checks it performs are * redundant with other checks in ReadArrayDimension() and ReadArrayStr() */ I deleted - nitems_according_to_dims = ArrayGetNItemsSafe(ndim, dim, escontext); - if (nitems_according_to_dims < 0) - PG_RETURN_NULL(); - if (nitems != nitems_according_to_dims) - elog(ERROR, "mismatch nitems, %d vs %d", nitems, nitems_according_to_dims); but I am not sure if the following is necessary. if (!ArrayCheckBoundsSafe(ndim, dim, lBound, escontext)) PG_RETURN_NULL(); I added some corner case tests like select '{{1,},{1},}'::text[]; some changes broken: select '{{1},{}}'::text[]; -DETAIL: Multidimensional arrays must have sub-arrays with matching dimensions. +DETAIL: Unexpected "," character. I added some error checks in ATOK_LEVEL_END. The first expect_delim part check will first generate an error, the dimension error part will not be reached. -
Re: Cleaning up array_in()
jian he <jian.universality@gmail.com> — 2023-09-04T00:00:00Z
hi. attached v4. v4, 0001 to 0005 is the same as v3 in https://www.postgresql.org/message-id/5859ce4e-2be4-92b0-c85c-e1e24eab57c6%40iki.fi v4-0006 doing some modifications to address the corner case mentioned in the previous thread (like select '{{1,},{1},}'::text[]). also fixed all these FIXME, Heikki mentioned in the code. v4-0007 refactor ReadDimensionInt. to make the array dimension bound variables within the INT_MIN and INT_MAX. so it will make select '[21474836488:21474836489]={1,2}'::int[]; fail. -
Re: Cleaning up array_in()
jian he <jian.universality@gmail.com> — 2023-09-04T23:53:13Z
On Mon, Sep 4, 2023 at 8:00 AM jian he <jian.universality@gmail.com> wrote: > > hi. > attached v4. > v4, 0001 to 0005 is the same as v3 in > https://www.postgresql.org/message-id/5859ce4e-2be4-92b0-c85c-e1e24eab57c6%40iki.fi > > v4-0006 doing some modifications to address the corner case mentioned > in the previous thread (like select '{{1,},{1},}'::text[]). > also fixed all these FIXME, Heikki mentioned in the code. > > v4-0007 refactor ReadDimensionInt. to make the array dimension bound > variables within the INT_MIN and INT_MAX. so it will make select > '[21474836488:21474836489]={1,2}'::int[]; fail. attached, same as v4, but delete unused variable {nitems_according_to_dims}. -
Re: Cleaning up array_in()
Alexander Law <exclusion@gmail.com> — 2023-09-10T10:00:00Z
Hello Jian, 05.09.2023 02:53, jian he wrote: > On Mon, Sep 4, 2023 at 8:00 AM jian he <jian.universality@gmail.com> wrote: >> hi. >> attached v4. >> v4, 0001 to 0005 is the same as v3 in >> https://www.postgresql.org/message-id/5859ce4e-2be4-92b0-c85c-e1e24eab57c6%40iki.fi >> >> v4-0006 doing some modifications to address the corner case mentioned >> in the previous thread (like select '{{1,},{1},}'::text[]). >> also fixed all these FIXME, Heikki mentioned in the code. >> >> v4-0007 refactor ReadDimensionInt. to make the array dimension bound >> variables within the INT_MIN and INT_MAX. so it will make select >> '[21474836488:21474836489]={1,2}'::int[]; fail. > > attached, same as v4, but delete unused variable {nitems_according_to_dims}. Please look at the differences, I've observed with the latest patches applied, old vs new behavior: Case 1: SELECT '{1,'::integer[]; ERROR: malformed array literal: "{1," LINE 1: SELECT '{1,'::integer[]; ^ DETAIL: Unexpected end of input. vs ERROR: malformed array literal: "{1," LINE 1: SELECT '{1,'::integer[]; ^ (no DETAIL) Case 2: SELECT '{{},}'::text[]; ERROR: malformed array literal: "{{},}" LINE 1: SELECT '{{},}'::text[]; ^ DETAIL: Unexpected "}" character vs text ------ {} (1 row) Case 3: select '{\{}'::text[]; text ------- {"{"} (1 row) vs text ------ {""} Best regards, Alexander -
Re: Cleaning up array_in()
jian he <jian.universality@gmail.com> — 2023-09-11T05:26:16Z
On Sun, Sep 10, 2023 at 6:00 PM Alexander Lakhin <exclusion@gmail.com> wrote: > Case 1: > SELECT '{1,'::integer[]; > ERROR: malformed array literal: "{1," > LINE 1: SELECT '{1,'::integer[]; > ^ > DETAIL: Unexpected end of input. > > vs > > ERROR: malformed array literal: "{1," > LINE 1: SELECT '{1,'::integer[]; > ^ > > (no DETAIL) > > Case 2: > SELECT '{{},}'::text[]; > ERROR: malformed array literal: "{{},}" > LINE 1: SELECT '{{},}'::text[]; > ^ > DETAIL: Unexpected "}" character > > vs > text > ------ > {} > (1 row) > > Case 3: > select '{\{}'::text[]; > text > ------- > {"{"} > (1 row) > > vs > text > ------ > {""} > > Best regards, > Alexander hi. Thanks for reviewing it. > DETAIL: Unexpected end of input. In many cases, ending errors will happen, so I consolidate it. SELECT '{{},}'::text[]; solved by tracking current token type and previous token type. select '{\{}'::text[]; solved by update dstendptr. attached. -
Re: Cleaning up array_in()
Alexander Law <exclusion@gmail.com> — 2023-09-11T12:00:00Z
11.09.2023 08:26, jian he wrote: > hi. > Thanks for reviewing it. > >> DETAIL: Unexpected end of input. > In many cases, ending errors will happen, so I consolidate it. > > SELECT '{{},}'::text[]; > solved by tracking current token type and previous token type. > > select '{\{}'::text[]; > solved by update dstendptr. > > attached. Thank you! I can confirm that all those anomalies are fixed now. But new version brings a warning when compiled with gcc: arrayfuncs.c:659:9: warning: variable 'prev_tok' is uninitialized when used here [-Wuninitialized] if (prev_tok == ATOK_DELIM || nest_level == 0) ^~~~~~~~ arrayfuncs.c:628:3: note: variable 'prev_tok' is declared here ArrayToken prev_tok; ^ 1 warning generated. Also it looks like an updated comment needs fixing/improving: /* No array dimensions, so first literal character should be oepn curl-braces */ (should be an opening brace?) (I haven't look at the code closely.) Best regards, Alexander -
Re: Cleaning up array_in()
jian he <jian.universality@gmail.com> — 2023-09-12T08:45:29Z
On Mon, Sep 11, 2023 at 8:00 PM Alexander Lakhin <exclusion@gmail.com> wrote: > > I can confirm that all those anomalies are fixed now. > But new version brings a warning when compiled with gcc: > arrayfuncs.c:659:9: warning: variable 'prev_tok' is uninitialized when used here [-Wuninitialized] > if (prev_tok == ATOK_DELIM || nest_level == 0) > ^~~~~~~~ > arrayfuncs.c:628:3: note: variable 'prev_tok' is declared here > ArrayToken prev_tok; > ^ > 1 warning generated. > > Also it looks like an updated comment needs fixing/improving: > /* No array dimensions, so first literal character should be oepn curl-braces */ > (should be an opening brace?) > fixed these 2 issues. --query SELECT ('{ ' || string_agg(chr((ascii('B') + round(random() * 25)) :: integer),', ') || ' }')::text[] FROM generate_series(1,1e6) \watch i=0.1 c=1 After applying the patch, the above query runs slightly faster. -
Re: Cleaning up array_in()
Alexander Law <exclusion@gmail.com> — 2023-09-13T06:00:00Z
12.09.2023 11:45, jian he wrote: > On Mon, Sep 11, 2023 at 8:00 PM Alexander Lakhin <exclusion@gmail.com> wrote: >> I can confirm that all those anomalies are fixed now. >> But new version brings a warning when compiled with gcc: >> arrayfuncs.c:659:9: warning: variable 'prev_tok' is uninitialized when used here [-Wuninitialized] >> if (prev_tok == ATOK_DELIM || nest_level == 0) >> ^~~~~~~~ >> arrayfuncs.c:628:3: note: variable 'prev_tok' is declared here >> ArrayToken prev_tok; >> ^ >> 1 warning generated. >> >> Also it looks like an updated comment needs fixing/improving: >> /* No array dimensions, so first literal character should be oepn curl-braces */ >> (should be an opening brace?) >> > fixed these 2 issues. > --query > SELECT ('{ ' || string_agg(chr((ascii('B') + round(random() * 25)) :: > integer),', ') || ' }')::text[] > FROM generate_series(1,1e6) \watch i=0.1 c=1 > > After applying the patch, the above query runs slightly faster. Thank you, Jian He! Now I see only a few wrinkles. 1) A minor asymmetry in providing details appeared: select E'{"a"a}'::text[]; ERROR: malformed array literal: "{"a"a}" LINE 1: select E'{"a"a}'::text[]; ^ DETAIL: Unexpected array element. select E'{a"a"}'::text[]; ERROR: malformed array literal: "{a"a"}" LINE 1: select E'{a"a"}'::text[]; ^ (no DETAIL) Old behavior: select E'{a"a"}'::text[]; ERROR: malformed array literal: "{a"a"}" LINE 1: select E'{a"a"}'::text[]; ^ DETAIL: Unexpected array element. select E'{"a"a}'::text[]; ERROR: malformed array literal: "{"a"a}" LINE 1: select E'{"a"a}'::text[]; ^ DETAIL: Unexpected array element. 2) CPPFLAGS="-DARRAYDEBUG" ./configure ... breaks "make check", maybe change elog(NOTICE) to elog(DEBUG1)? 2a) a message logged there lacks some delimiter before "lBound info": NOTICE: array_in- ndim 1 ( 3 -1 -1 -1 -1 -1lBound info 1 1 1 1 1 1) for {red,green,blue} what about changing the format to "ndim 1 ( 3 -1 -1 -1 -1 -1; lBound info: 1 1 1 1 1 1)"? 3) It seems that new comments need polishing, in particular: /* initialize dim, lBound. useful for ReadArrayDimensions ReadArrayStr */ ->? /* Initialize dim, lBound for ReadArrayDimensions, ReadArrayStr */ Otherwise, we determine the dimensions from the in curly-braces ->? Otherwise, we determine the dimensions from the curly braces. Best regards, Alexander -
Re: Cleaning up array_in()
jian he <jian.universality@gmail.com> — 2023-09-13T08:55:23Z
On Wed, Sep 13, 2023 at 2:00 PM Alexander Lakhin <exclusion@gmail.com> wrote: > > > Now I see only a few wrinkles. > 1) A minor asymmetry in providing details appeared: > select E'{"a"a}'::text[]; > ERROR: malformed array literal: "{"a"a}" > LINE 1: select E'{"a"a}'::text[]; > ^ > DETAIL: Unexpected array element. > > select E'{a"a"}'::text[]; > ERROR: malformed array literal: "{a"a"}" > LINE 1: select E'{a"a"}'::text[]; > ^ > (no DETAIL) > > Old behavior: > select E'{a"a"}'::text[]; > ERROR: malformed array literal: "{a"a"}" > LINE 1: select E'{a"a"}'::text[]; > ^ > DETAIL: Unexpected array element. > > select E'{"a"a}'::text[]; > ERROR: malformed array literal: "{"a"a}" > LINE 1: select E'{"a"a}'::text[]; > ^ > DETAIL: Unexpected array element. fixed and added these two query to the test. > 2) CPPFLAGS="-DARRAYDEBUG" ./configure ... breaks "make check", maybe change elog(NOTICE) to elog(DEBUG1)? > 2a) a message logged there lacks some delimiter before "lBound info": > NOTICE: array_in- ndim 1 ( 3 -1 -1 -1 -1 -1lBound info 1 1 1 1 1 1) for {red,green,blue} > what about changing the format to "ndim 1 ( 3 -1 -1 -1 -1 -1; lBound info: 1 1 1 1 1 1)"? fixed. Use elog(DEBUG1) now. > 3) It seems that new comments need polishing, in particular: > /* initialize dim, lBound. useful for ReadArrayDimensions ReadArrayStr */ > ->? > /* Initialize dim, lBound for ReadArrayDimensions, ReadArrayStr */ > > Otherwise, we determine the dimensions from the in curly-braces > ->? > Otherwise, we determine the dimensions from the curly braces. > > Best regards, > Alexander comments updates. please check the attached. -
Re: Cleaning up array_in()
Alexander Law <exclusion@gmail.com> — 2023-09-14T06:00:01Z
13.09.2023 11:55, jian he wrote: >> 2) CPPFLAGS="-DARRAYDEBUG" ./configure ... breaks "make check", maybe change elog(NOTICE) to elog(DEBUG1)? >> 2a) a message logged there lacks some delimiter before "lBound info": >> NOTICE: array_in- ndim 1 ( 3 -1 -1 -1 -1 -1lBound info 1 1 1 1 1 1) for {red,green,blue} >> what about changing the format to "ndim 1 ( 3 -1 -1 -1 -1 -1; lBound info: 1 1 1 1 1 1)"? > fixed. Use elog(DEBUG1) now. Thanks for the fixes! I didn't mean to remove the prefix "array_in-", but in fact I was confused by the "{function_name}-" syntax, and now when I've looked at it closely, I see that that syntax was quite popular ("date_in- ", "single_decode- ", ...) back in 1997 (see 9d8ae7977). But nowadays it is out of fashion, with most of such debugging prints were gone with 7a877dfd2 and the next-to-last one with 50861cd68. Moreover, as the latter commit shows, such debugging output can be eliminated completely without remorse. (And I couldn't find mentions of ARRAYDEBUG in pgsql-bugs, pgsql-hackers archives, so probably no one used that debugging facility since it's introduction.) As of now, the output still weird (I mean the excessive right parenthesis): DEBUG: ndim 1 ( 2 -1 -1 -1 -1 -1); lBound info: 1 1 1 1 1 1) for {0,0} Otherwise, from a user perspective, the patch set looks good to me. (Though maybe English language editorialization still needed before committing it.) Best regards, Alexander -
Re: Cleaning up array_in()
jian he <jian.universality@gmail.com> — 2023-09-14T08:14:01Z
On Thu, Sep 14, 2023 at 2:00 PM Alexander Lakhin <exclusion@gmail.com> wrote: > > > I didn't mean to remove the prefix "array_in-", but in fact I was confused > by the "{function_name}-" syntax, and now when I've looked at it closely, I > see that that syntax was quite popular ("date_in- ", "single_decode- ", ...) > back in 1997 (see 9d8ae7977). But nowadays it is out of fashion, with most > of such debugging prints were gone with 7a877dfd2 and the next-to-last one > with 50861cd68. Moreover, as the latter commit shows, such debugging output > can be eliminated completely without remorse. (And I couldn't find mentions > of ARRAYDEBUG in pgsql-bugs, pgsql-hackers archives, so probably no one used > that debugging facility since it's introduction.) > As of now, the output still weird (I mean the excessive right parenthesis): > DEBUG: ndim 1 ( 2 -1 -1 -1 -1 -1); lBound info: 1 1 1 1 1 1) for {0,0} > hi. similar to NUMERIC_DEBUG. I made the following adjustments. if unnecessary, removing this part seems also fine, in GDB, you can print it out directly. /* ---------- * Uncomment the following to get a dump of a array's ndim, dim, lBound. * ---------- #define ARRAYDEBUG */ #ifdef ARRAYDEBUG { StringInfoData buf; initStringInfo(&buf); appendStringInfo(&buf, "array_in- ndim %d, dim info(", ndim); for (int i = 0; i < MAXDIM; i++) appendStringInfo(&buf, " %d", dim[i]); appendStringInfo(&buf, "); lBound info("); for (int i = 0; i < MAXDIM; i++) appendStringInfo(&buf, " %d", lBound[i]); appendStringInfo(&buf, ") for %s", string); elog(DEBUG1, "%s", buf.data); pfree(buf.data); } #endif other than this, no other changes. -
Re: Cleaning up array_in()
jian he <jian.universality@gmail.com> — 2023-10-30T00:00:00Z
rebase after commit (https://git.postgresql.org/cgit/postgresql.git/commit/?id=611806cd726fc92989ac918eac48fd8d684869c7)
-
Re: Cleaning up array_in()
Tom Lane <tgl@sss.pgh.pa.us> — 2023-11-07T23:52:09Z
I got back to looking at this today (sorry for delay), and did a pass of code review. I think we are getting pretty close to something committable. The one loose end IMO is this bit in ReadArrayToken: + case '"': + + /* + * XXX "Unexpected %c character" would be more apropos, but + * this message is what the pre-v17 implementation produced, + * so we'll keep it for now. + */ + errsave(escontext, + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("malformed array literal: \"%s\"", origStr), + errdetail("Unexpected array element."))); + return ATOK_ERROR; This comes out when you write something like '{foo"bar"}', and I'd say the choice of message is not great. On the other hand, it's consistent with what you get from '{"foo""bar"}', and if we wanted to change that too then some tweaking of the state machine in ReadArrayStr would be required (or else modify ReadArrayToken so it doesn't return instantly upon seeing the second quote mark). I'm not sure that this is worth messing with. Anyway, I think we are well past the point where splitting the patch into multiple parts is worth doing, because we've rewritten pretty much all of this code, and the intermediate versions are not terribly helpful. So I just folded it all into one patch. Some notes about specific points: * Per previous discussion, I undid the change to allow "[1:0]" dimensions, but I left a comment behind about that. * Removing the ArrayGetNItemsSafe/ArrayCheckBoundsSafe calls seems OK, but then we need to be more careful about detecting overflows and disallowed cases in ReadArrayDimensions. * I don't think the ARRAYDEBUG code is of any value whatever. The fact that nobody bothered to improve it to print more than the dim[] values proves it hasn't been used in decades. Let's just nuke it. * We can simplify the state machine in ReadArrayStr some more: it seems to me it's sufficient to track "expect_delim", as long as you realize that that really means "expect typdelim or right brace". (Maybe another name would be better? I couldn't think of anything short though.) * I switched to using a StringInfo instead of a fixed-size elembuf, as Heikki speculated about. * I added some more test cases to cover things that evidently weren't sufficiently tested, like the has_escapes business which was flat out broken in v10, and to improve the code coverage report. Comments? regards, tom lane -
Re: Cleaning up array_in()
Alexander Law <exclusion@gmail.com> — 2023-11-08T15:00:00Z
Hello Tom, 08.11.2023 02:52, Tom Lane wrote: > Comments? Thank you for the update! I haven't looked into the code, just did manual testing and rechecked commands given in the arrays documentation ([1]). Everything works correctly, except for one minor difference: INSERT INTO sal_emp VALUES ('Bill', '{10000, 10000, 10000, 10000}', '{{"meeting", "lunch"}, {"meeting"}}'); currently gives: ERROR: malformed array literal: "{{"meeting", "lunch"}, {"meeting"}}" LINE 4: '{{"meeting", "lunch"}, {"meeting"}}'); ^ DETAIL: Multidimensional arrays must have sub-arrays with matching dimensions. not ERROR: multidimensional arrays must have array expressions with matching dimensions It seems that this inconsistency appeared with 475aedd1e, so it's not new at all, but maybe fix it or describe the error more generally. (Though it might be supposed that "for example" covers slight deviations.) [1] https://www.postgresql.org/docs/devel/arrays.html Best regards, Alexander -
Re: Cleaning up array_in()
Tom Lane <tgl@sss.pgh.pa.us> — 2023-11-08T15:56:57Z
Alexander Lakhin <exclusion@gmail.com> writes: > Thank you for the update! I haven't looked into the code, just did manual > testing and rechecked commands given in the arrays documentation ([1]). > Everything works correctly, except for one minor difference: > INSERT INTO sal_emp > VALUES ('Bill', > '{10000, 10000, 10000, 10000}', > '{{"meeting", "lunch"}, {"meeting"}}'); > currently gives: > ERROR: malformed array literal: "{{"meeting", "lunch"}, {"meeting"}}" > LINE 4: '{{"meeting", "lunch"}, {"meeting"}}'); > ^ > DETAIL: Multidimensional arrays must have sub-arrays with matching dimensions. > not > ERROR: multidimensional arrays must have array expressions with matching dimensions Oh! I had not realized we had actual documentation examples covering this area. Yeah, that doc needs to be updated to show the current wording of the error. Thanks for catching that. regards, tom lane -
Re: Cleaning up array_in()
Tom Lane <tgl@sss.pgh.pa.us> — 2023-11-09T16:57:47Z
I wrote: > This comes out when you write something like '{foo"bar"}', and I'd > say the choice of message is not great. On the other hand, it's > consistent with what you get from '{"foo""bar"}', and if we wanted > to change that too then some tweaking of the state machine in > ReadArrayStr would be required (or else modify ReadArrayToken so > it doesn't return instantly upon seeing the second quote mark). > I'm not sure that this is worth messing with. After further thought I concluded that this area is worth spending a little more code for. If we have input like '{foo"bar"}' or '{"foo"bar}' or '{"foo""bar"}', what it most likely means is that the user misunderstood the quoting rules. A message like "Unexpected array element" is pretty completely unhelpful for figuring that out. The alternative I was considering, "Unexpected """ character", would not be much better. What we want to say is something like "Incorrectly quoted array element", and the attached v12 makes ReadArrayToken do that for both quoted and unquoted cases. I also fixed the obsolete documentation that Alexander noted, and cleaned up a couple other infelicities (notably, I'd blindly written ereport(ERROR) in one place where ereturn is now the way). Barring objections, I think v12 is committable. regards, tom lane -
Re: Cleaning up array_in()
Heikki Linnakangas <hlinnaka@iki.fi> — 2023-11-12T23:30:52Z
On 09/11/2023 18:57, Tom Lane wrote: > After further thought I concluded that this area is worth spending > a little more code for. If we have input like '{foo"bar"}' or > '{"foo"bar}' or '{"foo""bar"}', what it most likely means is that > the user misunderstood the quoting rules. A message like "Unexpected > array element" is pretty completely unhelpful for figuring that out. > The alternative I was considering, "Unexpected """ character", would > not be much better. What we want to say is something like "Incorrectly > quoted array element", and the attached v12 makes ReadArrayToken do > that for both quoted and unquoted cases. +1 > I also fixed the obsolete documentation that Alexander noted, and > cleaned up a couple other infelicities (notably, I'd blindly written > ereport(ERROR) in one place where ereturn is now the way). > > Barring objections, I think v12 is committable. Looks good to me. Just two little things caught my eye: 1. > /* Initialize dim, lBound for ReadArrayDimensions, ReadArrayStr */ > for (int i = 0; i < MAXDIM; i++) > { > dim[i] = -1; /* indicates "not yet known" */ > lBound[i] = 1; /* default lower bound */ > } The function comments in ReadArrayDimensions and ReadArrayStr don't make it clear that these arrays need to be initialized like this. ReadArrayDimensions() says that they are output variables, and ReadArrayStr() doesn't mention anything about having to initialize them. 2. This was the same before this patch, but: postgres=# select '{{{{{{{{{{1}}}}}}}}}}'::int[]; ERROR: number of array dimensions (7) exceeds the maximum allowed (6) LINE 1: select '{{{{{{{{{{1}}}}}}}}}}'::int[]; ^ The error message isn't great, as the literal contains 10 dimensions, not 7 as the error message claims. -- Heikki Linnakangas Neon (https://neon.tech) -
Re: Cleaning up array_in()
Tom Lane <tgl@sss.pgh.pa.us> — 2023-11-13T00:11:48Z
Heikki Linnakangas <hlinnaka@iki.fi> writes: > On 09/11/2023 18:57, Tom Lane wrote: >> Barring objections, I think v12 is committable. > Looks good to me. Just two little things caught my eye: > 1. > The function comments in ReadArrayDimensions and ReadArrayStr don't make > it clear that these arrays need to be initialized like this. > ReadArrayDimensions() says that they are output variables, and > ReadArrayStr() doesn't mention anything about having to initialize them. Roger, will fix that. > 2. This was the same before this patch, but: > postgres=# select '{{{{{{{{{{1}}}}}}}}}}'::int[]; > ERROR: number of array dimensions (7) exceeds the maximum allowed (6) > LINE 1: select '{{{{{{{{{{1}}}}}}}}}}'::int[]; > ^ > The error message isn't great, as the literal contains 10 dimensions, > not 7 as the error message claims. Yeah. To make that report accurate, we'd have to somehow postpone issuing the error until we've seen all the left braces (or at least all the initial ones). There's a related problem in reading an explicitly-dimensioned array: postgres=# select '[1][2][3][4][5][6][7][8][9]={}'::text[]; ERROR: number of array dimensions (7) exceeds the maximum allowed (6) I kind of think it's not worth the trouble. What was discussed upthread was revising the message to not claim it knows how many dimensions there are. The related cases in plperl and plpython just say "number of array dimensions exceeds the maximum allowed (6)", and there's a case to be made for adjusting the core messages similarly. I figured that could be a separate patch though, since it'd touch more than array_in (there's about a dozen occurrences of the former wording). regards, tom lane -
Re: Cleaning up array_in()
Tom Lane <tgl@sss.pgh.pa.us> — 2023-11-13T18:23:30Z
I wrote: > Heikki Linnakangas <hlinnaka@iki.fi> writes: >> 2. This was the same before this patch, but: >> postgres=# select '{{{{{{{{{{1}}}}}}}}}}'::int[]; >> ERROR: number of array dimensions (7) exceeds the maximum allowed (6) >> LINE 1: select '{{{{{{{{{{1}}}}}}}}}}'::int[]; >> ^ >> The error message isn't great, as the literal contains 10 dimensions, >> not 7 as the error message claims. > Yeah. To make that report accurate, we'd have to somehow postpone > issuing the error until we've seen all the left braces (or at least > all the initial ones). There's a related problem in reading an > explicitly-dimensioned array: > postgres=# select '[1][2][3][4][5][6][7][8][9]={}'::text[]; > ERROR: number of array dimensions (7) exceeds the maximum allowed (6) > I kind of think it's not worth the trouble. What was discussed > upthread was revising the message to not claim it knows how many > dimensions there are. I pushed the main patch. Here's a proposed delta to deal with the bogus-dimensionality-count issue. There are a few more places where I left things alone because the code does know what the intended dimensionality will be; so there are still two versions of the translatable error message. regards, tom lane