Thread: jsonb_array_length: ERROR: 22023: cannot get array length of a scalar
Good afternoon,
in PostgreSQL 10.3 I have the following table with a jsonb column:
# \d words_moves;
Table "public.words_moves"
Column | Type | Collation | Nullable | Default
--------+--------------------------+-----------+----------+------------------------------------------
mid | bigint | | not null | nextval('words_moves_mid_seq'::regclass)
action | text | | not null |
gid | integer | | not null |
uid | integer | | not null |
played | timestamp with time zone | | not null |
tiles | jsonb | | |
score | integer | | |
Indexes:
"words_moves_pkey" PRIMARY KEY, btree (mid)
Check constraints:
"words_moves_score_check" CHECK (score >= 0)
Foreign-key constraints:
"words_moves_gid_fkey" FOREIGN KEY (gid) REFERENCES words_games(gid) ON DELETE CASCADE
"words_moves_uid_fkey" FOREIGN KEY (uid) REFERENCES words_users(uid) ON DELETE CASCADE
Referenced by:
TABLE "words_daily" CONSTRAINT "words_daily_mid_fkey" FOREIGN KEY (mid) REFERENCES words_moves(mid) ON DELETE CASCADE
TABLE "words_scores" CONSTRAINT "words_scores_mid_fkey" FOREIGN KEY (mid) REFERENCES words_moves(mid) ON DELETE CASCADE
Here are some records (please pardon the non-english chars):
# select * from words_moves where gid=609 limit 3;
-[ RECORD 1 ]----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
mid | 1040
action | play
gid | 609
uid | 1192
played | 2018-03-02 10:13:57.943876+01
tiles | [{"col": 3, "row": 7, "value": 2, "letter": "С"}, {"col": 4, "row": 7, "value": 1, "letter": "О"}, {"col": 5, "row": 7, "value": 2, "letter": "П"}, {"col": 6, "row": 7, "value": 0, "letter": "Л"}, {"col": 7, "row": 7, "value": 3, "letter": "Я"}]
score | 10
-[ RECORD 2 ]----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
mid | 1041
action | play
gid | 609
uid | 7
played | 2018-03-02 10:56:58.72503+01
tiles | [{"col": 3, "row": 8, "value": 2, "letter": "В"}, {"col": 3, "row": 9, "value": 1, "letter": "И"}, {"col": 3, "row": 10, "value": 2, "letter": "Т"}, {"col": 3, "row": 11, "value": 2, "letter": "К"}, {"col": 3, "row": 12, "value": 1, "letter": "А"}]
score | 14
-[ RECORD 3 ]----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
mid | 1043
action | play
gid | 609
uid | 1192
played | 2018-03-02 11:03:58.614094+01
tiles | [{"col": 0, "row": 10, "value": 2, "letter": "С"}, {"col": 1, "row": 10, "value": 2, "letter": "М"}, {"col": 2, "row": 10, "value": 1, "letter": "О"}, {"col": 4, "row": 10, "value": 2, "letter": "Р"}]
score | 13
I would like to get the length of the tiles array (because in my word game 7 played tiles mean +15 score bonus) - but that call fails for some reason:
# select mid, jsonb_array_length(tiles) from words_moves where gid=609;
ERROR: 22023: cannot get array length of a scalar
LOCATION: jsonb_array_length, jsonfuncs.c:1579
What am I doing wrong here please?
Regards
Alex
in PostgreSQL 10.3 I have the following table with a jsonb column:
# \d words_moves;
Table "public.words_moves"
Column | Type | Collation | Nullable | Default
--------+--------------------------+-----------+----------+------------------------------------------
mid | bigint | | not null | nextval('words_moves_mid_seq'::regclass)
action | text | | not null |
gid | integer | | not null |
uid | integer | | not null |
played | timestamp with time zone | | not null |
tiles | jsonb | | |
score | integer | | |
Indexes:
"words_moves_pkey" PRIMARY KEY, btree (mid)
Check constraints:
"words_moves_score_check" CHECK (score >= 0)
Foreign-key constraints:
"words_moves_gid_fkey" FOREIGN KEY (gid) REFERENCES words_games(gid) ON DELETE CASCADE
"words_moves_uid_fkey" FOREIGN KEY (uid) REFERENCES words_users(uid) ON DELETE CASCADE
Referenced by:
TABLE "words_daily" CONSTRAINT "words_daily_mid_fkey" FOREIGN KEY (mid) REFERENCES words_moves(mid) ON DELETE CASCADE
TABLE "words_scores" CONSTRAINT "words_scores_mid_fkey" FOREIGN KEY (mid) REFERENCES words_moves(mid) ON DELETE CASCADE
Here are some records (please pardon the non-english chars):
# select * from words_moves where gid=609 limit 3;
-[ RECORD 1 ]----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
mid | 1040
action | play
gid | 609
uid | 1192
played | 2018-03-02 10:13:57.943876+01
tiles | [{"col": 3, "row": 7, "value": 2, "letter": "С"}, {"col": 4, "row": 7, "value": 1, "letter": "О"}, {"col": 5, "row": 7, "value": 2, "letter": "П"}, {"col": 6, "row": 7, "value": 0, "letter": "Л"}, {"col": 7, "row": 7, "value": 3, "letter": "Я"}]
score | 10
-[ RECORD 2 ]----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
mid | 1041
action | play
gid | 609
uid | 7
played | 2018-03-02 10:56:58.72503+01
tiles | [{"col": 3, "row": 8, "value": 2, "letter": "В"}, {"col": 3, "row": 9, "value": 1, "letter": "И"}, {"col": 3, "row": 10, "value": 2, "letter": "Т"}, {"col": 3, "row": 11, "value": 2, "letter": "К"}, {"col": 3, "row": 12, "value": 1, "letter": "А"}]
score | 14
-[ RECORD 3 ]----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
mid | 1043
action | play
gid | 609
uid | 1192
played | 2018-03-02 11:03:58.614094+01
tiles | [{"col": 0, "row": 10, "value": 2, "letter": "С"}, {"col": 1, "row": 10, "value": 2, "letter": "М"}, {"col": 2, "row": 10, "value": 1, "letter": "О"}, {"col": 4, "row": 10, "value": 2, "letter": "Р"}]
score | 13
I would like to get the length of the tiles array (because in my word game 7 played tiles mean +15 score bonus) - but that call fails for some reason:
# select mid, jsonb_array_length(tiles) from words_moves where gid=609;
ERROR: 22023: cannot get array length of a scalar
LOCATION: jsonb_array_length, jsonfuncs.c:1579
What am I doing wrong here please?
Regards
Alex
On 03/02/2018 05:52 AM, Alexander Farber wrote: > Good afternoon, > > in PostgreSQL 10.3 I have the following table with a jsonb column: > > # \d words_moves; > Table "public.words_moves" > Column | Type | Collation | Nullable | > Default > --------+--------------------------+-----------+----------+------------------------------------------ > mid | bigint | | not null | > nextval('words_moves_mid_seq'::regclass) > action | text | | not null | > gid | integer | | not null | > uid | integer | | not null | > played | timestamp with time zone | | not null | > tiles | jsonb | | | > score | integer | | | > Indexes: > "words_moves_pkey" PRIMARY KEY, btree (mid) > Check constraints: > "words_moves_score_check" CHECK (score >= 0) > Foreign-key constraints: > "words_moves_gid_fkey" FOREIGN KEY (gid) REFERENCES > words_games(gid) ON DELETE CASCADE > "words_moves_uid_fkey" FOREIGN KEY (uid) REFERENCES > words_users(uid) ON DELETE CASCADE > Referenced by: > TABLE "words_daily" CONSTRAINT "words_daily_mid_fkey" FOREIGN KEY > (mid) REFERENCES words_moves(mid) ON DELETE CASCADE > TABLE "words_scores" CONSTRAINT "words_scores_mid_fkey" FOREIGN KEY > (mid) REFERENCES words_moves(mid) ON DELETE CASCADE > > Here are some records (please pardon the non-english chars): > > # select * from words_moves where gid=609 limit 3; > -[ RECORD 1 > ]---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- > mid | 1040 > action | play > gid | 609 > uid | 1192 > played | 2018-03-02 10:13:57.943876+01 > tiles | [{"col": 3, "row": 7, "value": 2, "letter": "С"}, {"col": 4, > "row": 7, "value": 1, "letter": "О"}, {"col": 5, "row": 7, "value": 2, > "letter": "П"}, {"col": 6, "row": 7, "value": 0, "letter": "Л"}, {"col": > 7, "row": 7, "value": 3, "letter": "Я"}] > score | 10 > -[ RECORD 2 > ]---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- > mid | 1041 > action | play > gid | 609 > uid | 7 > played | 2018-03-02 10:56:58.72503+01 > tiles | [{"col": 3, "row": 8, "value": 2, "letter": "В"}, {"col": 3, > "row": 9, "value": 1, "letter": "И"}, {"col": 3, "row": 10, "value": 2, > "letter": "Т"}, {"col": 3, "row": 11, "value": 2, "letter": "К"}, > {"col": 3, "row": 12, "value": 1, "letter": "А"}] > score | 14 > -[ RECORD 3 > ]---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- > mid | 1043 > action | play > gid | 609 > uid | 1192 > played | 2018-03-02 11:03:58.614094+01 > tiles | [{"col": 0, "row": 10, "value": 2, "letter": "С"}, {"col": 1, > "row": 10, "value": 2, "letter": "М"}, {"col": 2, "row": 10, "value": 1, > "letter": "О"}, {"col": 4, "row": 10, "value": 2, "letter": "Р"}] > score | 13 > > I would like to get the length of the tiles array (because in my word > game 7 played tiles mean +15 score bonus) - but that call fails for some > reason: > > # select mid, jsonb_array_length(tiles) from words_moves where gid=609; > ERROR: 22023: cannot get array length of a scalar > LOCATION: jsonb_array_length, jsonfuncs.c:1579 > > What am I doing wrong here please? Are you sure all the values in tiles are correctly formatted because when I use jsonb_array_length with the provided data: test=# select jsonb_array_length( '[{"col": 3, "row": 7, "value": 2, "letter": "С"}, {"col": 4, "row": 7, "value": 1, "letter": "О"}, {"col": 5, "row": 7, "value": 2, "letter": "П"}, {"col": 6, "row": 7, "value": 0, "letter": "Л"}, {"col": 7, "row": 7, "value": 3, "letter": "Я"}]'); jsonb_array_length -------------------- 5 test=# select jsonb_array_length( '[{"col": 3, "row": 8, "value": 2, "letter": "В"}, {"col": 3, "row": 9, "value": 1, "letter": "И"}, {"col": 3, "row": 10, "value": 2, "letter": "Т"}, {"col": 3, "row": 11, "value": 2, "letter": "К"}, {"col": 3, "row": 12, "value": 1, "letter": "А"}]'); jsonb_array_length -------------------- 5 test=# select jsonb_array_length('[{"col": 0, "row": 10, "value": 2, "letter": "С"}, {"col": 1, "row": 10, "value": 2, "letter": "М"}, {"col": 2, "row": 10, "value": 1, "letter": "О"}, {"col": 4, "row": 10, "value": 2, "letter": "Р"}]'); jsonb_array_length -------------------- 4 it works. The error message would suggest there is data in tiles which is not an array but a scalar value. > > Regards > Alex -- Adrian Klaver adrian.klaver@aklaver.com
Re: jsonb_array_length: ERROR: 22023: cannot get array length of a scalar
From
Alexander Farber
Date:
Hi Adrian, thank you for the reply -
On Fri, Mar 2, 2018 at 3:05 PM, Adrian Klaver <adrian.klaver@aklaver.com> wrote:
Are you sure all the values in tiles are correctly formatted because when I use jsonb_array_length with the provided data:On 03/02/2018 05:52 AM, Alexander Farber wrote:
in PostgreSQL 10.3 I have the following table with a jsonb column:
# \d words_moves;
Table "public.words_moves"
Column | Type | Collation | Nullable | Default
--------+--------------------------+-----------+----------+- ------------------------------ -----------
mid | bigint | | not null | nextval('words_moves_mid_seq'::regclass)
action | text | | not null |
gid | integer | | not null |
uid | integer | | not null |
played | timestamp with time zone | | not null |
tiles | jsonb | | |
score | integer | | |
Indexes:
"words_moves_pkey" PRIMARY KEY, btree (mid)
Check constraints:
"words_moves_score_check" CHECK (score >= 0)
Foreign-key constraints:
"words_moves_gid_fkey" FOREIGN KEY (gid) REFERENCES words_games(gid) ON DELETE CASCADE
"words_moves_uid_fkey" FOREIGN KEY (uid) REFERENCES words_users(uid) ON DELETE CASCADE
Referenced by:
TABLE "words_daily" CONSTRAINT "words_daily_mid_fkey" FOREIGN KEY (mid) REFERENCES words_moves(mid) ON DELETE CASCADE
TABLE "words_scores" CONSTRAINT "words_scores_mid_fkey" FOREIGN KEY (mid) REFERENCES words_moves(mid) ON DELETE CASCADE
# select mid, jsonb_array_length(tiles) from words_moves where gid=609;
ERROR: 22023: cannot get array length of a scalar
LOCATION: jsonb_array_length, jsonfuncs.c:1579
What am I doing wrong here please?
test=# select jsonb_array_length( '[{"col": 3, "row": 7, "value": 2, "letter": "С"}, {"col": 4, "row": 7, "value": 1, "letter": "О"}, {"col": 5, "row": 7, "value": 2, "letter": "П"}, {"col": 6, "row": 7, "value": 0, "letter": "Л"}, {"col": 7, "row": 7, "value": 3, "letter": "Я"}]');
jsonb_array_length
--------------------
5
I fill that table with the following stored function (please pardon the huge listing):
CREATE OR REPLACE FUNCTION words_play_game(
in_uid integer,
in_gid integer,
in_tiles jsonb
) RETURNS table (
out_uid integer, -- the player to be notified
out_fcm text,
out_apns text,
out_adm text,
out_body text
) AS
$func$
DECLARE
_tile jsonb;
_letter char;
_value integer;
_col integer;
_row integer;
_pos integer;
_mid bigint;
_total integer;
_hand_len integer;
_pile_len integer;
_move_len integer;
_pile char[];
_hand char[];
_letters char[][];
_values integer[][];
_opponent integer;
_finished timestamptz;
_reason text;
_score1 integer;
_score2 integer;
BEGIN
IF EXISTS (SELECT 1 FROM words_users
WHERE uid = in_uid AND
banned_until > CURRENT_TIMESTAMP) THEN
RAISE EXCEPTION 'User % is banned', in_uid;
END IF;
-- fetch the 4 arrays (_hand, _pile, _letters, _values) for the current game
SELECT
hand1,
pile,
letters,
values
INTO
_hand,
_pile,
_letters,
_values
FROM words_games WHERE
gid = in_gid AND
player1 = in_uid AND
-- game is not over yet
finished IS NULL AND
-- and it is first player's turn
(played1 IS NULL OR played1 < played2);
IF NOT FOUND THEN
SELECT
hand2,
pile,
letters,
values
INTO
_hand,
_pile,
_letters,
_values
FROM words_games WHERE
gid = in_gid AND
player2 = in_uid AND
-- game is not over yet
finished IS NULL AND
-- and it is second player's turn
(played2 IS NULL OR played2 < played1);
END IF;
IF NOT FOUND THEN
RAISE EXCEPTION 'Game % not found for user %', in_gid, in_uid;
END IF;
PERFORM words_check_positions(in_uid, in_gid, in_tiles);
FOR _tile IN SELECT * FROM JSONB_ARRAY_ELEMENTS(in_tiles)
LOOP
_letter := _tile->>'letter';
_value := (_tile->>'value')::int;
_col := (_tile->>'col')::int + 1;
_row := (_tile->>'row')::int + 1;
IF NOT words_valid_tile(_letter, _value) THEN
RAISE EXCEPTION 'Invalid tile = %', _tile;
END IF;
-- search for the played tile in the player hand
IF _value = 0 THEN
_pos := ARRAY_POSITION(_hand, '*');
ELSE
_pos := ARRAY_POSITION(_hand, _letter);
END IF;
IF _pos >= 1 THEN
_hand[_pos] := NULL;
ELSE
RAISE EXCEPTION 'Tile % not found in hand %', _tile, _hand;
END IF;
_letters[_col][_row] := _letter;
_values[_col][_row] := _value;
END LOOP;
-- remove played tiles from player hand
_hand := ARRAY_REMOVE(_hand, NULL);
-- move up to 7 missing tiles from pile to hand
_hand_len := CARDINALITY(_hand);
_pile_len := CARDINALITY(_pile);
_move_len := LEAST(7 - _hand_len, _pile_len);
_hand := _hand || _pile[1:_move_len];
_pile := _pile[(1 + _move_len):_pile_len];
INSERT INTO words_moves (
action,
gid,
uid,
played,
tiles
) VALUES (
'play',
in_gid,
in_uid,
CURRENT_TIMESTAMP,
in_tiles
) RETURNING mid INTO STRICT _mid;
INSERT INTO words_scores (
mid,
gid,
uid,
word,
score
) ( SELECT
_mid,
in_gid,
in_uid,
out_word,
max(out_score)
FROM words_check_words(in_uid, in_gid, in_tiles)
GROUP BY out_word);
SELECT
SUM(score),
words_get_given(in_uid) || ': ' || STRING_AGG(FORMAT('%s (%s)', word, score), ', ')
INTO STRICT
_total,
out_body
FROM words_scores
WHERE mid = _mid;
if _move_len = 7 THEN
_total := _total + 15;
out_body := out_body || ' +15 бонус';
END IF;
-- player has no tiles, game over
IF CARDINALITY(_hand) = 0 THEN
_finished := CURRENT_TIMESTAMP;
_reason := 'regular';
-- TODO append win, loss, draw to out_body
END IF;
UPDATE words_moves SET
score = _total
WHERE mid = _mid;
-- RAISE NOTICE '_hand = %', _hand;
-- RAISE NOTICE '_pile = %', _pile;
-- RAISE NOTICE '_letters = %', _letters;
-- RAISE NOTICE '_values = %', _values;
-- RAISE NOTICE '_hand_len = %', _hand_len;
-- RAISE NOTICE '_pile_len = %', _pile_len;
-- RAISE NOTICE '_move_len = %', _move_len;
-- RAISE NOTICE '_total = %', _total;
-- TODO update score and store played words and stats
UPDATE words_games SET
finished = _finished,
reason = _reason,
played1 = CURRENT_TIMESTAMP,
score1 = score1 + _total,
hand1 = _hand,
pile = _pile,
letters = _letters,
values = _values,
state1 = words_get_state(_finished, score1 + _total, score2),
state2 = words_get_state(_finished, score2, score1 + _total),
hint1 = words_get_hint(_finished, FALSE, score1 + _total, score2),
hint2 = words_get_hint(_finished, TRUE, score2, score1 + _total)
WHERE
gid = in_gid AND
player1 = in_uid AND
-- game is not over yet
finished IS NULL AND
-- and it is first player's turn
(played1 IS NULL OR played1 < played2)
RETURNING
player2,
score1,
score2
INTO
_opponent,
_score1,
_score2;
IF NOT FOUND THEN
UPDATE words_games SET
finished = _finished,
reason = _reason,
played2 = CURRENT_TIMESTAMP,
score2 = score2 + _total,
hand2 = _hand,
pile = _pile,
letters = _letters,
values = _values,
state1 = words_get_state(_finished, score1, score2 + _total),
state2 = words_get_state(_finished, score2 + _total, score1),
hint1 = words_get_hint(_finished, TRUE, score1, score2 + _total),
hint2 = words_get_hint(_finished, FALSE, score2 + _total, score1)
WHERE
gid = in_gid AND
player2 = in_uid AND
-- game is not over yet
finished IS NULL AND
-- and it is second player's turn
(played2 IS NULL OR played2 < played1)
RETURNING
player1,
score2,
score1
INTO
_opponent,
_score1,
_score2;
END IF;
IF NOT FOUND THEN
RAISE EXCEPTION 'Game % not found for user %', in_gid, in_uid;
END IF;
-- this is the very first move in 1-player game, notification not needed
IF _opponent IS NULL THEN
RETURN;
END IF;
SELECT
_opponent,
fcm,
apns,
adm
FROM words_users
WHERE uid = _opponent
INTO STRICT
out_uid,
out_fcm,
out_apns,
out_adm;
-- add 1 row (containing notification) to the output table
RETURN NEXT;
END
$func$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION words_play_game(
in_uid integer,
in_gid integer,
in_tiles jsonb
) RETURNS table (
out_uid integer, -- the player to be notified
out_fcm text,
out_apns text,
out_adm text,
out_body text
) AS
$func$
DECLARE
_tile jsonb;
_letter char;
_value integer;
_col integer;
_row integer;
_pos integer;
_mid bigint;
_total integer;
_hand_len integer;
_pile_len integer;
_move_len integer;
_pile char[];
_hand char[];
_letters char[][];
_values integer[][];
_opponent integer;
_finished timestamptz;
_reason text;
_score1 integer;
_score2 integer;
BEGIN
IF EXISTS (SELECT 1 FROM words_users
WHERE uid = in_uid AND
banned_until > CURRENT_TIMESTAMP) THEN
RAISE EXCEPTION 'User % is banned', in_uid;
END IF;
-- fetch the 4 arrays (_hand, _pile, _letters, _values) for the current game
SELECT
hand1,
pile,
letters,
values
INTO
_hand,
_pile,
_letters,
_values
FROM words_games WHERE
gid = in_gid AND
player1 = in_uid AND
-- game is not over yet
finished IS NULL AND
-- and it is first player's turn
(played1 IS NULL OR played1 < played2);
IF NOT FOUND THEN
SELECT
hand2,
pile,
letters,
values
INTO
_hand,
_pile,
_letters,
_values
FROM words_games WHERE
gid = in_gid AND
player2 = in_uid AND
-- game is not over yet
finished IS NULL AND
-- and it is second player's turn
(played2 IS NULL OR played2 < played1);
END IF;
IF NOT FOUND THEN
RAISE EXCEPTION 'Game % not found for user %', in_gid, in_uid;
END IF;
PERFORM words_check_positions(in_uid, in_gid, in_tiles);
FOR _tile IN SELECT * FROM JSONB_ARRAY_ELEMENTS(in_tiles)
LOOP
_letter := _tile->>'letter';
_value := (_tile->>'value')::int;
_col := (_tile->>'col')::int + 1;
_row := (_tile->>'row')::int + 1;
IF NOT words_valid_tile(_letter, _value) THEN
RAISE EXCEPTION 'Invalid tile = %', _tile;
END IF;
-- search for the played tile in the player hand
IF _value = 0 THEN
_pos := ARRAY_POSITION(_hand, '*');
ELSE
_pos := ARRAY_POSITION(_hand, _letter);
END IF;
IF _pos >= 1 THEN
_hand[_pos] := NULL;
ELSE
RAISE EXCEPTION 'Tile % not found in hand %', _tile, _hand;
END IF;
_letters[_col][_row] := _letter;
_values[_col][_row] := _value;
END LOOP;
-- remove played tiles from player hand
_hand := ARRAY_REMOVE(_hand, NULL);
-- move up to 7 missing tiles from pile to hand
_hand_len := CARDINALITY(_hand);
_pile_len := CARDINALITY(_pile);
_move_len := LEAST(7 - _hand_len, _pile_len);
_hand := _hand || _pile[1:_move_len];
_pile := _pile[(1 + _move_len):_pile_len];
INSERT INTO words_moves (
action,
gid,
uid,
played,
tiles
) VALUES (
'play',
in_gid,
in_uid,
CURRENT_TIMESTAMP,
in_tiles
) RETURNING mid INTO STRICT _mid;
INSERT INTO words_scores (
mid,
gid,
uid,
word,
score
) ( SELECT
_mid,
in_gid,
in_uid,
out_word,
max(out_score)
FROM words_check_words(in_uid, in_gid, in_tiles)
GROUP BY out_word);
SELECT
SUM(score),
words_get_given(in_uid) || ': ' || STRING_AGG(FORMAT('%s (%s)', word, score), ', ')
INTO STRICT
_total,
out_body
FROM words_scores
WHERE mid = _mid;
if _move_len = 7 THEN
_total := _total + 15;
out_body := out_body || ' +15 бонус';
END IF;
-- player has no tiles, game over
IF CARDINALITY(_hand) = 0 THEN
_finished := CURRENT_TIMESTAMP;
_reason := 'regular';
-- TODO append win, loss, draw to out_body
END IF;
UPDATE words_moves SET
score = _total
WHERE mid = _mid;
-- RAISE NOTICE '_hand = %', _hand;
-- RAISE NOTICE '_pile = %', _pile;
-- RAISE NOTICE '_letters = %', _letters;
-- RAISE NOTICE '_values = %', _values;
-- RAISE NOTICE '_hand_len = %', _hand_len;
-- RAISE NOTICE '_pile_len = %', _pile_len;
-- RAISE NOTICE '_move_len = %', _move_len;
-- RAISE NOTICE '_total = %', _total;
-- TODO update score and store played words and stats
UPDATE words_games SET
finished = _finished,
reason = _reason,
played1 = CURRENT_TIMESTAMP,
score1 = score1 + _total,
hand1 = _hand,
pile = _pile,
letters = _letters,
values = _values,
state1 = words_get_state(_finished, score1 + _total, score2),
state2 = words_get_state(_finished, score2, score1 + _total),
hint1 = words_get_hint(_finished, FALSE, score1 + _total, score2),
hint2 = words_get_hint(_finished, TRUE, score2, score1 + _total)
WHERE
gid = in_gid AND
player1 = in_uid AND
-- game is not over yet
finished IS NULL AND
-- and it is first player's turn
(played1 IS NULL OR played1 < played2)
RETURNING
player2,
score1,
score2
INTO
_opponent,
_score1,
_score2;
IF NOT FOUND THEN
UPDATE words_games SET
finished = _finished,
reason = _reason,
played2 = CURRENT_TIMESTAMP,
score2 = score2 + _total,
hand2 = _hand,
pile = _pile,
letters = _letters,
values = _values,
state1 = words_get_state(_finished, score1, score2 + _total),
state2 = words_get_state(_finished, score2 + _total, score1),
hint1 = words_get_hint(_finished, TRUE, score1, score2 + _total),
hint2 = words_get_hint(_finished, FALSE, score2 + _total, score1)
WHERE
gid = in_gid AND
player2 = in_uid AND
-- game is not over yet
finished IS NULL AND
-- and it is second player's turn
(played2 IS NULL OR played2 < played1)
RETURNING
player1,
score2,
score1
INTO
_opponent,
_score1,
_score2;
END IF;
IF NOT FOUND THEN
RAISE EXCEPTION 'Game % not found for user %', in_gid, in_uid;
END IF;
-- this is the very first move in 1-player game, notification not needed
IF _opponent IS NULL THEN
RETURN;
END IF;
SELECT
_opponent,
fcm,
apns,
adm
FROM words_users
WHERE uid = _opponent
INTO STRICT
out_uid,
out_fcm,
out_apns,
out_adm;
-- add 1 row (containing notification) to the output table
RETURN NEXT;
END
$func$ LANGUAGE plpgsql;
Re: jsonb_array_length: ERROR: 22023: cannot get array length of a scalar
From
Alexander Farber
Date:
And here is how I call my stored function -
https://gist.github.com/afarber/88a832a1b90a8940764ad69b2b761914
- why wouldn't it store a jsonb array in the tiles column of words_moves table?https://gist.github.com/afarber/88a832a1b90a8940764ad69b2b761914
2018-03-02 15:29:42.644 CET [16693] LOG: statement: DISCARD ALL
2018-03-02 15:29:42.644 CET [16693] LOG: duration: 0.015 ms
2018-03-02 15:30:33.645 CET [16693] LOG: statement: select 1
2018-03-02 15:30:33.645 CET [16693] LOG: duration: 0.094 ms
2018-03-02 15:30:33.645 CET [16693] LOG: statement: SET DateStyle='ISO';
2018-03-02 15:30:33.645 CET [16693] LOG: duration: 0.050 ms
2018-03-02 15:30:33.645 CET [16693] LOG: duration: 0.021 ms parse <unnamed>: SET extra_float_digits = 3
2018-03-02 15:30:33.645 CET [16693] LOG: duration: 0.003 ms bind <unnamed>: SET extra_float_digits = 3
2018-03-02 15:30:33.645 CET [16693] LOG: execute <unnamed>: SET extra_float_digits = 3
2018-03-02 15:30:33.645 CET [16693] LOG: duration: 0.006 ms
2018-03-02 15:30:33.645 CET [16693] LOG: duration: 0.007 ms parse <unnamed>: SET application_name = 'PostgreSQL JDBC Driver'
2018-03-02 15:30:33.645 CET [16693] LOG: duration: 0.002 ms bind <unnamed>: SET application_name = 'PostgreSQL JDBC Driver'
2018-03-02 15:30:33.645 CET [16693] LOG: execute <unnamed>: SET application_name = 'PostgreSQL JDBC Driver'
2018-03-02 15:30:33.645 CET [16693] LOG: duration: 0.005 ms
2018-03-02 15:30:33.646 CET [16693] LOG: duration: 0.110 ms parse <unnamed>: SELECT out_uid AS uid, out_fcm AS fcm, out_apns AS apns, out_adm AS adm, out_body AS body FROM words_play_game($1::int, $2::int, $3::jsonb)
2018-03-02 15:30:33.646 CET [16693] LOG: duration: 0.058 ms bind <unnamed>: SELECT out_uid AS uid, out_fcm AS fcm, out_apns AS apns, out_adm AS adm, out_body AS body FROM words_play_game($1::int, $2::int, $3::jsonb)
2018-03-02 15:30:33.646 CET [16693] DETAIL: parameters: $1 = '7', $2 = '609', $3 = '[{"col":0,"letter":"К","row":3,"value":2},{"col":0,"letter":"И","row":4,"value":1}]'
2018-03-02 15:30:33.646 CET [16693] LOG: execute <unnamed>: SELECT out_uid AS uid, out_fcm AS fcm, out_apns AS apns, out_adm AS adm, out_body AS body FROM words_play_game($1::int, $2::int, $3::jsonb)
2018-03-02 15:30:33.646 CET [16693] DETAIL: parameters: $1 = '7', $2 = '609', $3 = '[{"col":0,"letter":"К","row":3,"value":2},{"col":0,"letter":"И","row":4,"value":1}]'
2018-03-02 15:30:33.653 CET [16693] LOG: duration: 7.567 ms
On 03/02/2018 06:14 AM, Alexander Farber wrote: > Hi Adrian, thank you for the reply - > > # select mid, jsonb_array_length(tiles) from words_moves where > gid=609; > ERROR: 22023: cannot get array length of a scalar > LOCATION: jsonb_array_length, jsonfuncs.c:1579 > > What am I doing wrong here please? > > > Are you sure all the values in tiles are correctly formatted because > when I use jsonb_array_length with the provided data: > > test=# select jsonb_array_length( '[{"col": 3, "row": 7, "value": 2, > "letter": "С"}, {"col": 4, "row": 7, "value": 1, "letter": "О"}, > {"col": 5, "row": 7, "value": 2, "letter": "П"}, {"col": 6, "row": > 7, "value": 0, "letter": "Л"}, {"col": 7, "row": 7, "value": 3, > "letter": "Я"}]'); > jsonb_array_length > -------------------- > 5 > > > I fill that table with the following stored function (please pardon the > huge listing): The little gray cells are not awake enough to work through the below:) If it where me I would first confirm there was malformed data by looking at the data itself. If there are not that many records for gid = 609 maybe a simple select of tiles would be sufficient. Otherwise maybe a simple plpgsql function that loops through the records applying jsonb_array_length and raising a notice on the error. In any case the point is to identify the presence of malformed data and if present the nature of the malformation. That would help reverse engineer any issues with below. > > CREATE OR REPLACE FUNCTION words_play_game( > in_uid integer, > in_gid integer, > in_tiles jsonb > ) RETURNS table ( > out_uid integer, -- the player to be notified > out_fcm text, > out_apns text, > out_adm text, > out_body text > ) AS -- Adrian Klaver adrian.klaver@aklaver.com
Re: jsonb_array_length: ERROR: 22023: cannot get array length of a scalar
From
Alexander Farber
Date:
Hi Adrian, I 100% agree that nobody except me should debug my huge stored function, but if you look at my PostgreSQL 10.3 log -
On Fri, Mar 2, 2018 at 3:31 PM, Adrian Klaver <adrian.klaver@aklaver.com> wrote:
2018-03-02 15:30:33.646 CET [16693] LOG: duration: 0.110 ms parse <unnamed>: SELECT out_uid AS uid, out_fcm AS fcm, out_apns AS apns, out_adm AS adm, out_body AS body FROM words_play_game($1::int, $2::int, $3::jsonb)
2018-03-02 15:30:33.646 CET [16693] LOG: duration: 0.058 ms bind <unnamed>: SELECT out_uid AS uid, out_fcm AS fcm, out_apns AS apns, out_adm AS adm, out_body AS body FROM words_play_game($1::int, $2::int, $3::jsonb)
2018-03-02 15:30:33.646 CET [16693] DETAIL: parameters: $1 = '7', $2 = '609', $3 = '[{"col":0,"letter":"К","row": 3,"value":2},{"col":0,"letter" :"И","row":4,"value":1}]'
2018-03-02 15:30:33.646 CET [16693] LOG: execute <unnamed>: SELECT out_uid AS uid, out_fcm AS fcm, out_apns AS apns, out_adm AS adm, out_body AS body FROM words_play_game($1::int, $2::int, $3::jsonb)
2018-03-02 15:30:33.646 CET [16693] DETAIL: parameters: $1 = '7', $2 = '609', $3 = '[{"col":0,"letter":"К","row": 3,"value":2},{"col":0,"letter" :"И","row":4,"value":1}]'
The little gray cells are not awake enough to work through the below:) If it where me I would first confirm there was malformed data by looking at the data itself. If there are not that many records for gid = 609 maybe a simple select of tiles would be sufficient. Otherwise maybe a simple plpgsql function that loops through the records applying jsonb_array_length and raising a notice on the error. In any case the point is to identify the presence of malformed data and if present the nature of the malformation. That would help reverse engineer any issues with below.
CREATE OR REPLACE FUNCTION words_play_game(
in_uid integer,
in_gid integer,
in_tiles jsonb
) RETURNS table (
out_uid integer, -- the player to be notified
out_fcm text,
out_apns text,
out_adm text,
out_body text
) AS
2018-03-02 15:30:33.646 CET [16693] LOG: duration: 0.110 ms parse <unnamed>: SELECT out_uid AS uid, out_fcm AS fcm, out_apns AS apns, out_adm AS adm, out_body AS body FROM words_play_game($1::int, $2::int, $3::jsonb)
2018-03-02 15:30:33.646 CET [16693] LOG: duration: 0.058 ms bind <unnamed>: SELECT out_uid AS uid, out_fcm AS fcm, out_apns AS apns, out_adm AS adm, out_body AS body FROM words_play_game($1::int, $2::int, $3::jsonb)
2018-03-02 15:30:33.646 CET [16693] DETAIL: parameters: $1 = '7', $2 = '609', $3 = '[{"col":0,"letter":"К","row":
2018-03-02 15:30:33.646 CET [16693] LOG: execute <unnamed>: SELECT out_uid AS uid, out_fcm AS fcm, out_apns AS apns, out_adm AS adm, out_body AS body FROM words_play_game($1::int, $2::int, $3::jsonb)
2018-03-02 15:30:33.646 CET [16693] DETAIL: parameters: $1 = '7', $2 = '609', $3 = '[{"col":0,"letter":"К","row":
I just pass as the 3rd argument in_tiles to my stored function: '[{"col":0,"letter":"К","row": 3,"value":2},{"col":0,"letter" :"И","row":4,"value":1}]'
and then take the in_tiles and store it unchanged in the words_moves table:
INSERT INTO words_moves (
action,
gid,
uid,
played,
tiles
) VALUES (
'play',
in_gid,
in_uid,
CURRENT_TIMESTAMP,
in_tiles
) RETURNING mid INTO STRICT _mid;
INSERT INTO words_moves (
action,
gid,
uid,
played,
tiles
) VALUES (
'play',
in_gid,
in_uid,
CURRENT_TIMESTAMP,
in_tiles
) RETURNING mid INTO STRICT _mid;
Does anybody happen to see what could I do wrong there?
Thank you for any hints
Alex
P.S: Here my stored fuinction: https://gist.github.com/afarber/88a832a1b90a8940764ad69b2b761914
And here is how I call the stored function from Java:
String SQL_PLAY_GAME =
"SELECT " +
"out_uid AS uid, " +
"out_fcm AS fcm, " +
"out_apns AS apns, " +
"out_adm AS adm, " +
"out_body AS body " +
"FROM words_play_game(?::int, ?::int, ?::jsonb)";
private void handlePlay(int gid, String tiles) throws SQLException, IOException {
LOG.info("handlePlay: {} -> {} {}", mUid, gid, tiles);
try (Connection db = DriverManager.getConnection(DATABASE_URL, DATABASE_USER, DATABASE_PASS);
PreparedStatement st = db.prepareStatement(SQL_PLAY_GAME)) {
st.setInt(1, mUid);
st.setInt(2, gid);
st.setString(3, tiles);
runPlayerAction(st, gid);
}
}
String SQL_PLAY_GAME =
"SELECT " +
"out_uid AS uid, " +
"out_fcm AS fcm, " +
"out_apns AS apns, " +
"out_adm AS adm, " +
"out_body AS body " +
"FROM words_play_game(?::int, ?::int, ?::jsonb)";
private void handlePlay(int gid, String tiles) throws SQLException, IOException {
LOG.info("handlePlay: {} -> {} {}", mUid, gid, tiles);
try (Connection db = DriverManager.getConnection(DATABASE_URL, DATABASE_USER, DATABASE_PASS);
PreparedStatement st = db.prepareStatement(SQL_PLAY_GAME)) {
st.setInt(1, mUid);
st.setInt(2, gid);
st.setString(3, tiles);
runPlayerAction(st, gid);
}
}
On 03/02/2018 06:42 AM, Alexander Farber wrote: > Hi Adrian, I 100% agree that nobody except me should debug my huge > stored function, but if you look at my PostgreSQL 10.3 log - > Which proves what has already been proven, that at least some of the data is correct. The issue is data that is not correct as evidenced by the error message: select mid, jsonb_array_length(tiles) from words_moves where gid=609; ERROR: 22023: cannot get array length of a scalar LOCATION: jsonb_array_length, jsonfuncs.c:1579 This is not going to get solved until you identify the 'bad' tiles data. > 2018-03-02 15:30:33.646 CET [16693] LOG: duration: 0.110 ms parse > <unnamed>: SELECT out_uid AS uid, out_fcm AS fcm, out_apns AS apns, > out_adm AS adm, out_body AS body FROM words_play_game($1::int, > $2::int, $3::jsonb) > 2018-03-02 15:30:33.646 CET [16693] LOG: duration: 0.058 ms bind > <unnamed>: SELECT out_uid AS uid, out_fcm AS fcm, out_apns AS apns, > out_adm AS adm, out_body AS body FROM words_play_game($1::int, > $2::int, $3::jsonb) > 2018-03-02 15:30:33.646 CET [16693] DETAIL: parameters: $1 = '7', $2 = > '609', $3 = > '[{"col":0,"letter":"К","row":3,"value":2},{"col":0,"letter":"И","row":4,"value":1}]' > 2018-03-02 15:30:33.646 CET [16693] LOG: execute <unnamed>: SELECT > out_uid AS uid, out_fcm AS fcm, out_apns AS apns, out_adm AS adm, > out_body AS body FROM words_play_game($1::int, $2::int, $3::jsonb) > 2018-03-02 15:30:33.646 CET [16693] DETAIL: parameters: $1 = '7', $2 = > '609', $3 = > '[{"col":0,"letter":"К","row":3,"value":2},{"col":0,"letter":"И","row":4,"value":1}]' > > I just pass as the 3rd argument in_tiles to my stored function: > '[{"col":0,"letter":"К","row":3,"value":2},{"col":0,"letter":"И","row":4,"value":1}]' > > and then take the in_tiles and store it unchanged in the words_moves table: > > INSERT INTO words_moves ( > action, > gid, > uid, > played, > tiles > ) VALUES ( > 'play', > in_gid, > in_uid, > CURRENT_TIMESTAMP, > in_tiles > ) RETURNING mid INTO STRICT _mid; > > Does anybody happen to see what could I do wrong there? > > Thank you for any hints > Alex > > P.S: Here my stored fuinction: > https://gist.github.com/afarber/88a832a1b90a8940764ad69b2b761914 > Here my table: > https://gist.github.com/afarber/06cc37114ff8dd14f05077f312904361 > And here is how I call the stored function from Java: > > String SQL_PLAY_GAME = > "SELECT " + > "out_uid AS uid, " + > "out_fcm AS fcm, " + > "out_apns AS apns, " + > "out_adm AS adm, " + > "out_body AS body " + > "FROM words_play_game(?::int, ?::int, ?::jsonb)"; > > private void handlePlay(int gid, String tiles) throws SQLException, > IOException { > LOG.info("handlePlay: {} -> {} {}", mUid, gid, tiles); > try (Connection db = DriverManager.getConnection(DATABASE_URL, > DATABASE_USER, DATABASE_PASS); > PreparedStatement st = > db.prepareStatement(SQL_PLAY_GAME)) { > st.setInt(1, mUid); > st.setInt(2, gid); > st.setString(3, tiles); > runPlayerAction(st, gid); > } > } -- Adrian Klaver adrian.klaver@aklaver.com
On 03/02/2018 06:42 AM, Alexander Farber wrote: > Hi Adrian, I 100% agree that nobody except me should debug my huge > stored function, but if you look at my PostgreSQL 10.3 log - > Another thought, in : 2018-03-02 15:30:33.646 CET [16693] LOG: duration: 0.058 ms bind <unnamed>: SELECT out_uid AS uid, out_fcm AS fcm, out_apns AS apns, out_adm AS adm, out_body AS body FROM words_play_game($1::int, $2::int, $3::jsonb) where is $3::jsonb coming from? -- Adrian Klaver adrian.klaver@aklaver.com
Re: jsonb_array_length: ERROR: 22023: cannot get array length of a scalar
From
"David G. Johnston"
Date:
On Friday, March 2, 2018, Adrian Klaver <adrian.klaver@aklaver.com> wrote:
On 03/02/2018 06:42 AM, Alexander Farber wrote:
2018-03-02 15:30:33.646 CET [16693] LOG: duration: 0.058 ms bind <unnamed>: SELECT out_uid AS uid, out_fcm AS fcm, out_apns AS apns, out_adm AS adm, out_body AS body FROM words_play_game($1::int, $2::int, $3::jsonb)
where is $3::jsonb coming from?
Java prepared statement I think, using setString.
Not at computer right now but what does:
'[1,2,3]'::text::jsonb result in compared to '[1,2,3]'::jsonb ?
Because the system think your array-looking string is actually just a scalar that happens to have a leading [ and a trailing ]
David J.
On 03/02/2018 10:04 AM, David G. Johnston wrote: > On Friday, March 2, 2018, Adrian Klaver <adrian.klaver@aklaver.com > <mailto:adrian.klaver@aklaver.com>> wrote: > > On 03/02/2018 06:42 AM, Alexander Farber wrote: > > > > 2018-03-02 15:30:33.646 CET [16693] LOG: duration: 0.058 ms bind > <unnamed>: SELECT out_uid AS uid, out_fcm AS fcm, out_apns AS > apns, out_adm AS adm, out_body AS body FROM > words_play_game($1::int, $2::int, $3::jsonb) > > where is $3::jsonb coming from? > > > Java prepared statement I think, using setString. I was thinking more about the ultimate source of the data. The words_play_game() function, AFAICT, just passes the jsonb from input into the word_moves table. If that is the case the problem may occur further upstream where the jsonb array is actually built. > > Not at computer right now but what does: > > '[1,2,3]'::text::jsonb result in compared to '[1,2,3]'::jsonb ? > > Because the system think your array-looking string is actually just a > scalar that happens to have a leading [ and a trailing ] > > David J. > -- Adrian Klaver adrian.klaver@aklaver.com
Re: jsonb_array_length: ERROR: 22023: cannot get array length of a scalar
From
Alexander Farber
Date:
Good evening -
On Fri, Mar 2, 2018 at 7:11 PM, Adrian Klaver <adrian.klaver@aklaver.com> wrote:
that argument comes over Websocket - either from my Android app,
I was thinking more about the ultimate source of the data. The words_play_game() function, AFAICT, just passes the jsonb from input into the word_moves table. If that is the case the problem may occur further upstream where the jsonb array is actually built.
2018-03-02 15:30:33.646 CET [16693] LOG: duration: 0.058 ms bind
<unnamed>: SELECT out_uid AS uid, out_fcm AS fcm, out_apns AS
apns, out_adm AS adm, out_body AS body FROM
words_play_game($1::int, $2::int, $3::jsonb)
where is $3::jsonb coming from?
or from my HTML5 game at https://slova.de/words/Words.js (just an array of objects, stringified):
var tiles = [];
for (var i = boardTiles.length - 1; i >= 0; i--) {
var tile = boardTiles[i];
tiles.push({
letter: tile.letter,
value: tile.value,
col: tile.col,
row: tile.row
});
}
var play = {
social: SOCIAL,
sid: SID,
auth: AUTH,
action: 'play',
gid: gid,
tiles: tiles
};
ws.send(JSON.stringify(play));
var tiles = [];
for (var i = boardTiles.length - 1; i >= 0; i--) {
var tile = boardTiles[i];
tiles.push({
letter: tile.letter,
value: tile.value,
col: tile.col,
row: tile.row
});
}
var play = {
social: SOCIAL,
sid: SID,
auth: AUTH,
action: 'play',
gid: gid,
tiles: tiles
};
ws.send(JSON.stringify(play));
Regards
Alex
Re: jsonb_array_length: ERROR: 22023: cannot get array length of a scalar
From
Alexander Farber
Date:
# select * from words_moves where gid=609 limit 3;
-[ RECORD 1 ]----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
mid | 1040
action | play
gid | 609
uid | 1192
played | 2018-03-02 10:13:57.943876+01
tiles | [{"col": 3, "row": 7, "value": 2, "letter": "С"}, {"col": 4, "row": 7, "value": 1, "letter": "О"}, {"col": 5, "row": 7, "value": 2, "letter": "П"}, {"col": 6, "row": 7, "value": 0, "letter": "Л"}, {"col": 7, "row": 7, "value": 3, "letter": "Я"}]
score | 10
-[ RECORD 2 ]----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
mid | 1041
action | play
gid | 609
uid | 7
played | 2018-03-02 10:56:58.72503+01
tiles | [{"col": 3, "row": 8, "value": 2, "letter": "В"}, {"col": 3, "row": 9, "value": 1, "letter": "И"}, {"col": 3, "row": 10, "value": 2, "letter": "Т"}, {"col": 3, "row": 11, "value": 2, "letter": "К"}, {"col": 3, "row": 12, "value": 1, "letter": "А"}]
score | 14
-[ RECORD 3 ]----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
mid | 1043
action | play
gid | 609
uid | 1192
played | 2018-03-02 11:03:58.614094+01
tiles | [{"col": 0, "row": 10, "value": 2, "letter": "С"}, {"col": 1, "row": 10, "value": 2, "letter": "М"}, {"col": 2, "row": 10, "value": 1, "letter": "О"}, {"col": 4, "row": 10, "value": 2, "letter": "Р"}]
score | 13
# select column_name, data_type from information_schema.columns where table_name='words_moves';
column_name | data_type
-------------+--------------------------
mid | bigint
action | text
gid | integer
uid | integer
played | timestamp with time zone
tiles | jsonb
score | integer
(7 rows)
# select jsonb_array_length(tiles) from words_moves where gid=609 limit 3;
jsonb_array_length
--------------------
5
5
4
(3 rows)
BUT:
# select jsonb_array_length(tiles) from words_moves where gid=609 ;
ERROR: 22023: cannot get array length of a scalar
LOCATION: jsonb_array_length, jsonfuncs.c:1579
Which means only some data is bad, but how to find it please?
Re: jsonb_array_length: ERROR: 22023: cannot get array length of a scalar
From
Alexander Farber
Date:
Oops, I've got strings there too - when swapping instead of playing tiles:
# select * from words_moves where gid=609 and action <> 'play';
mid | action | gid | uid | played | tiles | score
------+--------+-----+------+-------------------------------+----------+-------
1063 | swap | 609 | 1192 | 2018-03-02 14:13:24.684301+01 | "ТСНЦУЭ" | ¤
1067 | swap | 609 | 1192 | 2018-03-02 15:31:14.378474+01 | "ЕЯУЕФП" | ¤
1068 | swap | 609 | 7 | 2018-03-02 15:52:07.629119+01 | "ОЕЕАУ" | ¤
1072 | swap | 609 | 7 | 2018-03-02 16:06:43.365012+01 | "ЕЕЫ" | ¤
1076 | swap | 609 | 7 | 2018-03-02 16:20:18.933948+01 | "Ъ" | ¤
(5 rows)
So my problem was simple. I am sorry for the numerous mails!
(it is just such a long chain - android/html5 - Jetty - PostgreSQL, so that sometimes I am confused.
At least I am glad to have most of my logic as Pl/pgSQL right at the data)
Regards
Alex
Re: jsonb_array_length: ERROR: 22023: cannot get array length of a scalar
From
"David G. Johnston"
Date:
Oops, I've got strings there too - when swapping instead of playing tiles:
You should probably add:
jsonb_array_length(tiles) > 0
as a check constraint on column
David J.
On 03/02/2018 10:43 AM, Alexander Farber wrote: > # select * from words_moves where gid=609 limit 3; > -[ RECORD 1 > ]---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- > mid | 1040 > action | play > gid | 609 > uid | 1192 > played | 2018-03-02 10:13:57.943876+01 > tiles | [{"col": 3, "row": 7, "value": 2, "letter": "С"}, {"col": 4, > "row": 7, "value": 1, "letter": "О"}, {"col": 5, "row": 7, "value": 2, > "letter": "П"}, {"col": 6, "row": 7, "value": 0, "letter": "Л"}, {"col": > 7, "row": 7, "value": 3, "letter": "Я"}] > score | 10 > -[ RECORD 2 > ]---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- > mid | 1041 > action | play > gid | 609 > uid | 7 > played | 2018-03-02 10:56:58.72503+01 > tiles | [{"col": 3, "row": 8, "value": 2, "letter": "В"}, {"col": 3, > "row": 9, "value": 1, "letter": "И"}, {"col": 3, "row": 10, "value": 2, > "letter": "Т"}, {"col": 3, "row": 11, "value": 2, "letter": "К"}, > {"col": 3, "row": 12, "value": 1, "letter": "А"}] > score | 14 > -[ RECORD 3 > ]---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- > mid | 1043 > action | play > gid | 609 > uid | 1192 > played | 2018-03-02 11:03:58.614094+01 > tiles | [{"col": 0, "row": 10, "value": 2, "letter": "С"}, {"col": 1, > "row": 10, "value": 2, "letter": "М"}, {"col": 2, "row": 10, "value": 1, > "letter": "О"}, {"col": 4, "row": 10, "value": 2, "letter": "Р"}] > score | 13 > > # select column_name, data_type from information_schema.columns where > table_name='words_moves'; > column_name | data_type > -------------+-------------------------- > mid | bigint > action | text > gid | integer > uid | integer > played | timestamp with time zone > tiles | jsonb > score | integer > (7 rows) > > # select jsonb_array_length(tiles) from words_moves where gid=609 limit 3; > jsonb_array_length > -------------------- > 5 > 5 > 4 > (3 rows) > > BUT: > > # select jsonb_array_length(tiles) from words_moves where gid=609 ; > ERROR: 22023: cannot get array length of a scalar > LOCATION: jsonb_array_length, jsonfuncs.c:1579 > > > Which means only some data is bad, but how to find it please? What is? : select count(*) from words_moves where gid=609; A simplistic approach would be: select mid, jsonb_array_length(tiles) from words_moves where gid=609 order by mid limit x; where you increment x until you trigger the error. A more sophisticated approach would be to use plpgsql EXCEPTION handling: https://www.postgresql.org/docs/10/static/plpgsql-control-structures.html#PLPGSQL-ERROR-TRAPPING and create a function that loops through: select jsonb_array_length(tiles) from words_moves where gid=609 ; and RAISES a NOTICE for each incorrect value along with its mid value. > > > -- Adrian Klaver adrian.klaver@aklaver.com
Re: jsonb_array_length: ERROR: 22023: cannot get array length of a scalar
From
Alexander Farber
Date:
I see, thank you for your comments, David and Adrian.
In the "tiles" column actually save either the JSON array of tiles - when the user plays them
Or a string (which is jsonb too) concatenated of letters - when the user swaps the letters.
Maybe I should rethink my table structure (I just want to "log" all plays, swaps, skips, resigns in the words_moves table)...
Or maybe I should always check for the "action" column first (acts as enum) - before accessing "tiles" column....
Regrads
Alex
Re: jsonb_array_length: ERROR: 22023: cannot get array length of a scalar
From
"David G. Johnston"
Date:
I see, thank you for your comments, David and Adrian.In the "tiles" column actually save either the JSON array of tiles - when the user plays themOr a string (which is jsonb too) concatenated of letters - when the user swaps the letters.Maybe I should rethink my table structure (I just want to "log" all plays, swaps, skips, resigns in the words_moves table)...Or maybe I should always check for the "action" column first (acts as enum) - before accessing "tiles" column....
A table constraint like:
CASE WHEN action = 'Play'
THEN lengh < 0
WHEN action = 'Swap'
THEN ...
ELSE false
END
Is perfectly fine - though having actual non-null values take on different meanings based upon the enum is generally not a good idea. I've been using the above to enforce conditional not null constraints when I don't want to implement explicit inheritance.
David J.
On 03/02/2018 10:58 AM, Alexander Farber wrote: > I see, thank you for your comments, David and Adrian. > > In the "tiles" column actually save either the JSON array of tiles - > when the user plays them > > Or a string (which is jsonb too) concatenated of letters - when the user > swaps the letters. > > Maybe I should rethink my table structure (I just want to "log" all > plays, swaps, skips, resigns in the words_moves table)... Or make the string the value of an array: [{"swap": "ТСНЦУЭ"}] so you are not changing the inner JSON in the field. > > Or maybe I should always check for the "action" column first (acts as > enum) - before accessing "tiles" column.... > > Regrads > Alex > -- Adrian Klaver adrian.klaver@aklaver.com