Re: Please recommend postgresql.conf improvements for osm2pgsql loading Europe

2024-03-31 Thread Alexander Farber
Thank you for the insights


Re: Please recommend postgresql.conf improvements for osm2pgsql loading Europe

2024-03-30 Thread Alexander Farber
Turning fsync = off has resulted in no noticable build time reduction for
my Dockerfile with OSM Europe data, but still thank you for the suggestion!

>


Re: Please recommend postgresql.conf improvements for osm2pgsql loading Europe

2024-03-30 Thread Alexander Farber
Thank you, Justin -

On Sat, Mar 30, 2024 at 4:33 AM Justin Clift  wrote:

> On 2024-03-30 05:53, Alexander Farber wrote:
> > I use the following postgresql.conf in my Dockerfile
> > ( the full version at https://stackoverflow.com/a/78243530/165071 ),
> > when loading a 28 GByte large europe-latest.osm.pbf
>
> Not specific conf file improvements, but for an initial data load
> have you done things like turning off fsync(), deferring index
> creating until after the data load finishes, and that kind of thing?
>

I will try the following commands in my Dockerfile then
and later report back on any improvements:

RUN set -eux && \
pg_ctl init && \
echo "shared_buffers = 1GB">> $PGDATA/postgresql.conf
&& \
echo "work_mem = 50MB" >> $PGDATA/postgresql.conf
&& \
echo "maintenance_work_mem = 10GB" >> $PGDATA/postgresql.conf
&& \
echo "autovacuum_work_mem = 2GB"   >> $PGDATA/postgresql.conf
&& \
echo "wal_level = minimal" >> $PGDATA/postgresql.conf
&& \
echo "checkpoint_timeout = 60min"  >> $PGDATA/postgresql.conf
&& \
echo "max_wal_size = 10GB" >> $PGDATA/postgresql.conf
&& \
echo "checkpoint_completion_target = 0.9"  >> $PGDATA/postgresql.conf
&& \
echo "max_wal_senders = 0" >> $PGDATA/postgresql.conf
&& \
echo "random_page_cost = 1.0"  >> $PGDATA/postgresql.conf
&& \
echo "password_encryption = scram-sha-256" >> $PGDATA/postgresql.conf
&& \
echo "fsync = off">>
$PGDATA/postgresql.conf && \
pg_ctl start && \
createuser --username=postgres $PGUSER && \
createdb --username=postgres --encoding=UTF8 --owner=$PGUSER
$PGDATABASE && \
psql --username=postgres $PGDATABASE --command="ALTER USER $PGUSER WITH
PASSWORD '$PGPASSWORD';" && \
psql --username=postgres $PGDATABASE --command='CREATE EXTENSION IF NOT
EXISTS postgis;' && \
psql --username=postgres $PGDATABASE --command='CREATE EXTENSION IF NOT
EXISTS hstore;' && \
osm2pgsql --username=$PGUSER --database=$PGDATABASE --create
--cache=6 --hstore --latlong /data/map.osm.pbf && \
rm -f /data/map.osm.pbf && \
pg_ctl stop && \
echo "fsync = on">> $PGDATA/postgresql.conf
&& \
echo '# TYPE DATABASE USER ADDRESS METHOD'>
$PGDATA/pg_hba.conf && \
echo "local all postgres peer"   >>
$PGDATA/pg_hba.conf && \
echo "local $PGDATABASE $PGUSER   scram-sha-256" >>
$PGDATA/pg_hba.conf && \
echo "host  $PGDATABASE $PGUSER 0.0.0.0/0 scram-sha-256" >>
$PGDATA/pg_hba.conf

The later fsync = on will override the former, right?

Best regards
Alex


Please recommend postgresql.conf improvements for osm2pgsql loading Europe

2024-03-29 Thread Alexander Farber
Good evening,

I use the following postgresql.conf in my Dockerfile
( the full version at https://stackoverflow.com/a/78243530/165071 ),
when loading a 28 GByte large europe-latest.osm.pbf
into PostgreSQL 16 with PostGIS extension:

echo "shared_buffers = 1GB">> $PGDATA/postgresql.conf
&& \
echo "work_mem = 50MB" >> $PGDATA/postgresql.conf
&& \
echo "maintenance_work_mem = 10GB" >> $PGDATA/postgresql.conf
&& \
echo "autovacuum_work_mem = 2GB"   >> $PGDATA/postgresql.conf
&& \
echo "wal_level = minimal" >> $PGDATA/postgresql.conf
&& \
echo "checkpoint_timeout = 60min"  >> $PGDATA/postgresql.conf
&& \
echo "max_wal_size = 10GB" >> $PGDATA/postgresql.conf
&& \
echo "checkpoint_completion_target = 0.9"  >> $PGDATA/postgresql.conf
&& \
echo "max_wal_senders = 0" >> $PGDATA/postgresql.conf
&& \
echo "random_page_cost = 1.0"  >> $PGDATA/postgresql.conf
&& \
echo "password_encryption = scram-sha-256" >> $PGDATA/postgresql.conf
&& \

And after/during osm2pgsql is loading the data into the database,
the following messages are displayed by PostgreSQL:

2024-03-29T14:50:19.6909027Z 2024-03-29 14:50:19Processed 3355283586
nodes in 432s (7m 12s) - 7767k/s
2024-03-29T14:50:19.6909478Z 2024-03-29 14:50:19Processed 406646166
ways in 2720s (45m 20s) - 150k/s
2024-03-29T14:50:19.6909785Z 2024-03-29 14:50:19Processed 7265827
relations in 672s (11m 12s) - 11k/s
2024-03-29T14:50:20.0292733Z [0m [91m2024-03-29 14:50:20  node cache:
stored: 3355283586(100.00%), storage efficiency: 56.37% (dense blocks:
268264, sparse nodes: 1877553733), hit rate: 100.00%
2024-03-29T14:50:20.0293430Z 2024-03-29 14:50:20  Clustering table
'planet_osm_line' by geometry...
2024-03-29T14:50:20.0293787Z 2024-03-29 14:50:20  Clustering table
'planet_osm_point' by geometry...
2024-03-29T14:50:20.1056438Z [0m [91m2024-03-29 14:50:20  Clustering table
'planet_osm_polygon' by geometry...
2024-03-29T14:50:20.1056819Z 2024-03-29 14:50:20  Clustering table
'planet_osm_roads' by geometry...
2024-03-29T15:15:27.3834184Z [0m [91m2024-03-29 15:15:24  Creating geometry
index on table 'planet_osm_roads'...
2024-03-29T15:15:56.7850293Z [0m2024-03-29 15:15:56.784 UTC [344] LOG:
skipping vacuum of "planet_osm_roads" --- lock not available
2024-03-29T15:16:56.7864755Z 2024-03-29 15:16:56.785 UTC [347] LOG:
skipping vacuum of "planet_osm_roads" --- lock not available
2024-03-29T15:17:56.7891503Z 2024-03-29 15:17:56.788 UTC [350] LOG:
skipping vacuum of "planet_osm_roads" --- lock not available
2024-03-29T15:18:16.5830638Z [91m2024-03-29 15:18:16  Analyzing table
'planet_osm_roads'...
2024-03-29T15:18:18.1485666Z [0m [91m2024-03-29 15:18:18  All
postprocessing on table 'planet_osm_roads' done in 1678s (27m 58s).
2024-03-29T15:22:17.1463669Z [0m [91m2024-03-29 15:22:17  Creating geometry
index on table 'planet_osm_point'...
2024-03-29T15:22:56.8860848Z [0m2024-03-29 15:22:56.885 UTC [365] LOG:
skipping vacuum of "planet_osm_point" --- lock not available
2024-03-29T15:23:56.8890669Z 2024-03-29 15:23:56.888 UTC [368] LOG:
skipping vacuum of "planet_osm_point" --- lock not available
2024-03-29T15:24:56.9521355Z 2024-03-29 15:24:56.951 UTC [371] LOG:
skipping vacuum of "planet_osm_point" --- lock not available
2024-03-29T15:25:56.9166127Z 2024-03-29 15:25:56.915 UTC [374] LOG:
skipping vacuum of "planet_osm_point" --- lock not available
2024-03-29T15:26:57.4606148Z 2024-03-29 15:26:57.460 UTC [377] LOG:
skipping vacuum of "planet_osm_point" --- lock not available
2024-03-29T15:27:56.9555832Z 2024-03-29 15:27:56.955 UTC [380] LOG:
skipping vacuum of "planet_osm_point" --- lock not available
2024-03-29T15:28:56.9718398Z 2024-03-29 15:28:56.971 UTC [383] LOG:
skipping vacuum of "planet_osm_point" --- lock not available
2024-03-29T15:29:56.9912768Z 2024-03-29 15:29:56.990 UTC [386] LOG:
skipping vacuum of "planet_osm_point" --- lock not available
2024-03-29T15:30:57.1482580Z 2024-03-29 15:30:57.147 UTC [389] LOG:
skipping vacuum of "planet_osm_point" --- lock not available
2024-03-29T15:31:01.2674499Z [91m2024-03-29 15:31:01  Analyzing table
'planet_osm_point'...
2024-03-29T15:31:02.3310950Z [0m [91m2024-03-29 15:31:02  All
postprocessing on table 'planet_osm_point' done in 2442s (40m 42s).
2024-03-29T15:41:13.1544481Z [0m [91m2024-03-29 15:41:13  Creating geometry
index on table 'planet_osm_line'...
2024-03-29T15:41:57.2434689Z [0m2024-03-29 15:41:57.242 UTC [422] LOG:
skipping vacuum of "planet_osm_line" --- lock not available
2024-03-29T15:42:57.2519273Z 2024-03-29 15:42:57.251 UTC [425] LOG:
skipping vacuum of "planet_osm_line" --- lock not available
2024-03-29T15:43:57.2626514Z 2024-03-29 15:43:57.262 UTC [428] LOG:
skipping vacuum of "planet_osm_line" --- lock not available
2024-03-29T15:44:57.2531401Z 2024-03-29 15:44:57.252 UTC [431] LOG:
skipping vacuum of "planet_osm_line" --- lock not available

Re: [jetty-users] Run multiple instances of the same WAR servlet and pass it a config value

2023-10-04 Thread Alexander Farber via jetty-users
Apologies, the getServletConfig().getContextPath() in the init() of my
servlet works well, that probably had been a problem in my VS Code.

Still I wonder, if I would set a "property" with a "name" and "value" as
described in the "Jetty XML Syntax" section
https://eclipse.dev/jetty/documentation/jetty-10/operations-guide/index.html#og-xml
- how could I retrieve the value in the servlet?

Best regards
Alex
___
jetty-users mailing list
jetty-users@eclipse.org
To unsubscribe from this list, visit 
https://www.eclipse.org/mailman/listinfo/jetty-users


Re: [jetty-users] Run multiple instances of the same WAR servlet and pass it a config value

2023-10-04 Thread Alexander Farber via jetty-users
I try to use the following but the method is not available:

@Override
public void init() throws ServletException {
super.init();

mLanguage = getServletConfig().getContextPath();
   }
___
jetty-users mailing list
jetty-users@eclipse.org
To unsubscribe from this list, visit 
https://www.eclipse.org/mailman/listinfo/jetty-users


[jetty-users] Run multiple instances of the same WAR servlet and pass it a config value

2023-10-04 Thread Alexander Farber via jetty-users
Good morning,

I have an "overengineered" setup with Jetty 10.0.16 and haproxy 2.7.8,
where I currently run 6 different Jetty instances to serve the same servlet
-

I have programmed that WAR servlet using Maven and it supports 6 languages:

src/main/resources/strings.properties
src/main/resources/strings_de.properties
src/main/resources/strings_fr.properties
src/main/resources/strings_nl.properties
src/main/resources/strings_pl.properties
src/main/resources/strings_ru.properties

and then in the configure method I fetch the language value from the
"COUNTRY" env var:

@Override
public void configure(JettyWebSocketServletFactory factory) {
mLanguage = System.getenv("COUNTRY");
mBundle = ResourceBundle.getBundle("strings",
LOCALES.get(mLanguage));

factory.setIdleTimeout(Duration.ofSeconds(IDLE_TIMEOUT_SECONDS));
factory.setMaxBinaryMessageSize(0);
factory.setMaxTextMessageSize(64 * 1024);
factory.setCreator(new WordsCreator(this,
mBundle.getString(STR_DATABASE_URL)));
}

Then I have the following 6 scripts on my Rocky Linux 8.8 to start the
Jetty instances

/etc/systemd/system/jetty-de.service
/etc/systemd/system/jetty-en.service
/etc/systemd/system/jetty-fr.service
/etc/systemd/system/jetty-nl.service
/etc/systemd/system/jetty-pl.service
/etc/systemd/system/jetty-ru.service

And the only differences the scripts have is the port and the "COUNTRY" env
var:

[Unit]
Description=Jetty
After=network-online.target

[Service]
Environment=COUNTRY=de

Type=simple
User=jetty
Group=jetty
ExecStart=/usr/bin/java -Djdbc.drivers=org.postgresql.Driver -jar
/usr/share/java/jetty-home-10.0.16/start.jar
jetty.home=/usr/share/java/jetty-home-10.0.16
jetty.base=/var/www/jetty-base-de jetty.http.host=127.0.0.1
jetty.http.port=8081
SuccessExitStatus=143
Restart=always
RestartSec=180
PrivateTmp=true

[Install]
WantedBy=multi-user.target

In the /etc/haproxy/haproxy.cfg I look at the request path and forward the
request to one of the ports:

backend jetty_de
server domain 127.0.0.1:8081 send-proxy
backend jetty_en
server domain 127.0.0.1:8082 send-proxy
backend jetty_fr
server domain 127.0.0.1:8083 send-proxy
backend jetty_nl
server domain 127.0.0.1:8084 send-proxy
backend jetty_pl
server domain 127.0.0.1:8085 send-proxy
backend jetty_ru
server domain 127.0.0.1:8080 send-proxy

frontend wordsbyfarber_com
bind 95.216.113.90:80
bind 95.216.113.90:443 ssl crt
/etc/pki/tls/certs/wordsbyfarber.com.pem no-sslv3

redirect scheme https if !{ ssl_fc }

use_backend jetty_de if { path_beg /de }
use_backend jetty_en if { path_beg /en }
use_backend jetty_fr if { path_beg /fr }
use_backend jetty_nl if { path_beg /nl }
use_backend jetty_pl if { path_beg /pl }
use_backend jetty_ru if { path_beg /ru }

default_backend jetty_en

Finaly my config XML files look like this to "bind" the servlet to a
certain context path:

# cat /var/www/jetty-base-de/webapps/de.xml

http://www.eclipse.org/jetty/configure_9_0.dtd;>


/de
/var/www/words-5.0.war



Thank you for reading my mail, it is a bit longer, because I am trying to
provide enough details.

My question is, if it is possible to handle my task with a single Jetty
instance?

Could I set the env var in the  /var/www/jetty-base-de/webapps/de.xml file?

Or maybe alternatively access the value of "contextPath" from the
configure() method?

Thank you for any suggestions
Alex
___
jetty-users mailing list
jetty-users@eclipse.org
To unsubscribe from this list, visit 
https://www.eclipse.org/mailman/listinfo/jetty-users


Re: [jetty-users] Setting text/javascript encoding to utf8 with Jetty 10.0.16

2023-10-03 Thread Alexander Farber via jetty-users
Thank you, Joakim - your second suggestion has worked well for me:

 webapps/root.xml

http://www.eclipse.org/jetty/configure_9_0.dtd;>

/


index.html




/var/www/html/wordsbyfarber.com
true




js
text/javascript;charset=utf-8




Now I get the following response headers and the file is displayed with
correct international characters in the Microsoft Edge browser:

HTTP/1.1 200 OK
last-modified: Tue, 03 Oct 2023 09:52:40 GMT
content-type: text/javascript;charset=utf-8
accept-ranges: bytes
vary: Accept-Encoding
content-encoding: gzip
server: Jetty(10.0.16)
connection: close

Best regards
Alex
___
jetty-users mailing list
jetty-users@eclipse.org
To unsubscribe from this list, visit 
https://www.eclipse.org/mailman/listinfo/jetty-users


Re: [jetty-users] Setting text/javascript encoding to utf8 with Jetty 10.0.16

2023-10-03 Thread Alexander Farber via jetty-users
Thank you Joakim for the extensive answer, however -

On Tue, Oct 3, 2023 at 2:23 PM Joakim Erdfelt via jetty-users <
jetty-users@eclipse.org> wrote:

> Per the javascript RFC, the `text/javascript` mime-type has an optional
> `charset` parameter.
> The behavior is documented at
> https://datatracker.ietf.org/doc/html/rfc9239#name-charset-parameter
>
> Essentially, if the charset is unspecified, then the encoding is UTF-8.
>
> > the encoding is unfortunately not set to utf8 (like it is for the served
> json files).
>
> For JSON, the charset parameter is not used.
> Per spec, JSON is always UTF-8.
> See: https://www.rfc-editor.org/rfc/rfc8259#section-8.1
>
> In Jetty, the json encoding is specified as an assumed UTF-8.
> See:
> https://github.com/eclipse/jetty.project/blob/jetty-10.0.16/jetty-http/src/main/resources/org/eclipse/jetty/http/encoding.properties
> This means the `charset` parameter is not produced when generating the
> `Content-Type` header, and is ignored when parsing the `Content-Type`
> header.
>
>  > Is there a way to enforce that without compiling a custom version of
> Jetty?
>
> You can customize the in-place `MimeTypes` for a context.
>
> servletContextHandler.getMimeTypes().addMimeMapping("txt",
> "text/javascript;charset=UTF-8");
> or
> webappContext.getMimeTypes().addMimeMapping("txt",
> "text/javascript;charset=UTF-8");
>
> or, If you have a WEB-INF/web.xml in your webapp, you can add a
> `` entry.
>
>   
> js
> text/javascript;charset=UTF-8
>   
>

I have problems that my UTF8 file is displayed wrongly in the browser -

  https://wordsbyfarber.com/Consts-ru.js

The browser displays pairs of junk characters instead of cyrillic letters
and from my (limited) experience having ";charset=utf8" in the header would
help it.

So I have followed your suggestion and have extended the
src/main/webapp/WEB-INF/web.xml with:


http://xmlns.jcp.org/xml/ns/javaee;
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance;
 xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd;>

WordsServlet
de.afarber.WordsServlet


WordsServlet
/


js
text/javascript;charset=UTF-8



Unfortunately the URL still returns the headers without "UTF-8" in there:

HTTP/1.1 200 OK
last-modified: Tue, 03 Oct 2023 09:52:40 GMT
content-type: text/javascript
accept-ranges: bytes
vary: Accept-Encoding
content-encoding: gzip
server: Jetty(10.0.16)
connection: close

Best regards
Alex
___
jetty-users mailing list
jetty-users@eclipse.org
To unsubscribe from this list, visit 
https://www.eclipse.org/mailman/listinfo/jetty-users


[jetty-users] Setting text/javascript encoding to utf8 with Jetty 10.0.16

2023-10-03 Thread Alexander Farber via jetty-users
Hello,

I am using Jetty 10.0.16 and when it serves static JavaScript files (I have
Consts-en.js, Consts-de.js, Consts-fr.js, ...) the encoding is
unfortunately not set to utf8 (like it is for the served json files).

Is there a way to enforce that without compiling a custom version of Jetty?

I have searched on the internet and also here:

# find /usr/share/java/jetty-home-10.0.16 -type f -iname \*.prop\*
/usr/share/java/jetty-home-10.0.16/modules/deprecated.properties
/usr/share/java/jetty-home-10.0.16/modules/demo.d/demo-realm.properties
/usr/share/java/jetty-home-10.0.16/modules/demo.d/demo-login.properties
/usr/share/java/jetty-home-10.0.16/modules/sessions/infinispan/remote/resources/hotrod-client.properties
/usr/share/java/jetty-home-10.0.16/modules/jolokia/jolokia-realm.properties
/usr/share/java/jetty-home-10.0.16/modules/logging/jul/resources/java-util-logging-bridge.properties
/usr/share/java/jetty-home-10.0.16/modules/logging/jul/resources/java-util-logging.properties
/usr/share/java/jetty-home-10.0.16/modules/logging/jetty/resources/jetty-logging.properties
/usr/share/java/jetty-home-10.0.16/etc/jdbcRealm.properties

Thank you for any hints
Alex
___
jetty-users mailing list
jetty-users@eclipse.org
To unsubscribe from this list, visit 
https://www.eclipse.org/mailman/listinfo/jetty-users


Should I add a new schema for my application?

2023-04-15 Thread Alexander Farber
Good morning,

I am seeking for a general advice if it would be a good idea for a
PostgreSQL/PostGIS using application to add a third schema.

I am using postgis/postgis Docker image which comes with "public" schema.

Then I have loaded OpenStreetMap data into the database and have created
"osm_schema" for that:

# psql -p 5432 -U postgres

CREATE DATABASE osm_database TEMPLATE=template_postgis;
CREATE USER osm_username WITH ENCRYPTED PASSWORD 'osm_password';
GRANT ALL PRIVILEGES ON DATABASE osm_database TO osm_username; -- TODO
read only

# psql -p 5432 -U osm_username osm_database

CREATE SCHEMA osm_schema AUTHORIZATION osm_username;
ALTER ROLE osm_username SET search_path TO osm_schema, public;

After loading OSM data with the osm2pgsql tool I have 3 tables in there:

# psql -p 5432 -U osm_username osm_database
psql (15.2)

osm_database=> \dt
   List of relations
   Schema   |Name| Type  |Owner
++---+--
 osm_schema | planet_osm_line| table | osm_username
 osm_schema | planet_osm_point   | table | osm_username
 osm_schema | planet_osm_polygon | table | osm_username
 osm_schema | planet_osm_roads   | table | osm_username
 public | spatial_ref_sys| table | postgres
(5 rows)

And now I need one more table, for the new mapping feature in my
application, which would reference the osm_id column in the
planet_osm_roads.

I have not used database schemas at all until yet, I am looking for an
advice if I should add a third schema here, just for my application?

Would it be a good long term idea and what advantages/disadvantages would
that bring with it?

Thank you for any feedback and also I have to note, that I am really amazed
by all these software products -

PostgreSQL, PostGIS and also the OpenStreetMap - how smooth the
installation has worked for me - just an hour spent and I have the whole
planet at my fingertips - this is not coming for free, there are probably
decades of effort by many people in the products.

Best regards
Alex


Re: Displaying chat by punished users only to themselves (db fiddle attached)

2022-05-05 Thread Alexander Farber
Thank you, that was it!


Re: Displaying chat by punished users only to themselves (db fiddle attached)

2022-05-05 Thread Alexander Farber
Good evening, I still have a problem with my JOIN expression -
when I add more games, then messages from other games are displayed:

https://dbfiddle.uk/?rdbms=postgres_14=e2ff211f59090d1eeab879498148f907

CREATE OR REPLACE FUNCTION words_get_chat(
in_gid   integer,
in_uid   integer
) RETURNS TABLE (
out_mine integer,
out_game text,
out_msg  text
) AS
$func$
SELECT
CASE WHEN c.uid = in_uid THEN 1 ELSE 0 END,
'game #' || c.gid,
c.msg
FROMwords_chat c
JOINwords_games g USING (gid)
JOINwords_users opponent ON (opponent.uid IN (g.player1,
g.player2) AND opponent.uid <> in_uid)
WHERE   c.gid = in_gid
-- always show myself my own chat messages
AND c.uid = in_uid
-- otherwise only show messages by not muted opponents
OR  NOT opponent.muted
ORDER BY c.created ASC;

$func$ LANGUAGE sql;

I have tried making the JOIN words_users opponent even more restrictive
with:

JOINwords_users opponent ON (opponent.uid IN (g.player1,
g.player2) AND in_uid IN (g.player1, g.player2) AND opponent.uid <> in_uid)

but still messages from the game #20 are displayed, even though I pass
in_gid = 10

Best regards
Alex


Re: Displaying chat by punished users only to themselves (db fiddle attached)

2022-05-05 Thread Alexander Farber
Good morning, this is a very insightful comment (among many) by you, David -

On Wed, May 4, 2022 at 5:40 PM David G. Johnston 
wrote:

> Well, that is basically why I was going on about the oddity of having
> social be a part of the main query.  Personally I would write it as
> "myself.uid = in_uid", but you don't have an in_uid to reference.  Decide
> how you want to do something equivalent.
>
>
so I will rewrite the stored functions in my game to be like that, to
separate auth from functionality -

https://dbfiddle.uk/?rdbms=postgres_14=dbdf1a6b82f7232be45e23b8139a8f0e

CREATE OR REPLACE FUNCTION words_get_uid(
in_social integer,
in_sidtext
) RETURNS integer AS
$func$
SELECT uid
FROM words_social
WHERE social = in_social
AND sid = in_sid;
$func$ LANGUAGE sql IMMUTABLE;

CREATE OR REPLACE FUNCTION words_get_chat(
in_gid   integer,
in_uid   integer
) RETURNS TABLE (
out_mine integer,
out_msg  text
) AS
$func$
SELECT
CASE WHEN c.uid = in_uid THEN 1 ELSE 0 END,
c.msg
FROMwords_chat c
JOINwords_games g USING (gid)
JOINwords_users opponent ON (opponent.uid IN (g.player1,
g.player2) AND opponent.uid <> in_uid)
WHERE   c.gid = in_gid
-- always show myself my own chat messages
AND c.uid = in_uid
-- otherwise only show messages by not muted opponents
OR  NOT opponent.muted
ORDER BY c.created ASC;

$func$ LANGUAGE sql;

SELECT words_get_chat(10, words_get_uid(100, 'abc')) AS nice_user;

SELECT words_get_chat(10, words_get_uid(200, 'def')) AS muted_user;

Thanks
Alex


Re: Displaying chat by punished users only to themselves (db fiddle attached)

2022-05-04 Thread Alexander Farber
David, thanks but what do you mean by the last comment -

On Wed, May 4, 2022 at 7:44 PM David G. Johnston 
wrote:

> Using (SELECT uid FROM myself) provides the same result without the
> from/join reference; the usage in the case and the where clause could be
> rewritten to use opponent.uid so myself.uid only appears once.
>
>
I have applied your first 2 comments in

https://dbfiddle.uk/?rdbms=postgres_14=c3982c2b5e71369f3c92ee0c06dc29bf

WITH myself AS (
SELECT uid
FROM words_social
WHERE social = in_social
AND sid = in_sid
)
SELECT
CASE WHEN c.uid = myself.uid THEN 1 ELSE 0 END,
c.msg
FROMwords_chat c
JOINmyself ON TRUE
JOINwords_games g USING (gid)
JOINwords_users opponent ON (opponent.uid IN (g.player1,
g.player2) AND opponent.uid <> myself.uid)
WHERE   c.gid = in_gid
-- always show myself my own chat messages
AND c.uid = myself.uid
-- otherwise only show messages by not muted opponents
OR  NOT opponent.muted
ORDER BY c.created ASC;

but where to put the (SELECT uid FROM myself), I do not understand?


Re: Displaying chat by punished users only to themselves (db fiddle attached)

2022-05-04 Thread Alexander Farber
Is that the right way to do it?

https://dbfiddle.uk/?rdbms=postgres_14=7bd74243397da61ddc4c216ad919c7cc

WITH myself AS (
SELECT uid
FROM words_social
WHERE social = in_social
AND sid = in_sid
LIMIT 1
)
SELECT
CASE WHEN c.uid = myself.uid THEN 1 ELSE 0 END,
c.msg
FROMmyself
JOINwords_chat c ON TRUE
JOINwords_games g USING (gid)
JOINwords_users opponent ON (opponent.uid IN (g.player1,
g.player2) AND opponent.uid <> myself.uid)
WHERE   c.gid = in_gid
-- always show myself my own chat messages
AND c.uid = myself.uid
-- otherwise only show messages by not muted opponents
OR  NOT opponent.muted
ORDER BY c.created ASC;


Re: Displaying chat by punished users only to themselves (db fiddle attached)

2022-05-04 Thread Alexander Farber
I think I am very close with the following CTE, but do not understand how
to bring it into the main SELECT query:

https://dbfiddle.uk/?rdbms=postgres_14=ee264dc98b44dee75aa4523164a327b3

WITH myself AS (
SELECT uid
FROM words_social
WHERE social = in_social
AND sid = in_sid
LIMIT 1
)
SELECT
CASE WHEN c.uid = myself.uid THEN 1 ELSE 0 END,
c.msg
FROMwords_chat c
JOINwords_games g USING (gid)
JOINwords_users opponent ON (opponent.uid IN (g.player1,
g.player2) AND opponent.uid <> myself.uid)
WHERE   c.gid = in_gid
-- always show myself my own chat messages
AND c.uid = myself.uid
-- otherwise only show messages by not muted opponents
OR  NOT opponent.muted
ORDER BY c.CREATED ASC;

The error message is:

ERROR:  missing FROM-clause entry for table "myself"
LINE 64: ...uid IN (g.player1, g.player2) AND opponent.uid <> myself.uid...
  ^


Re: Displaying chat by punished users only to themselves (db fiddle attached)

2022-05-04 Thread Alexander Farber
I have tried CROSS JOIN and CASE WHEN (why be greedy, right?):

https://dbfiddle.uk/?rdbms=postgres_14=43a33374d15a9330145007702138822c

WITH myself AS (
SELECT uid
FROM words_social
WHERE social = in_social
AND sid = in_sid
LIMIT 1
),
opponent AS (
 SELECT CASE WHEN player1 = myself.uid THEN player2 ELSE
player1 END
 FROM words_games
 WHERE gid = in_gid
)
SELECT
CASE WHEN c.uid = myself.uid THEN 1 ELSE 0 END,
c.msg
FROM  myself CROSS JOIN opponent
WHERE   (c.uid = myself.uid OR NOT opponent.muted)
ORDER BY c.CREATED ASC;

but the error is:

ERROR:  missing FROM-clause entry for table "myself"
LINE 60:  SELECT CASE WHEN player1 = myself.uid THEN play...
 ^


Re: Displaying chat by punished users only to themselves (db fiddle attached)

2022-05-04 Thread Alexander Farber
I try with a CTE but cannot figure the syntax:

https://dbfiddle.uk/?rdbms=postgres_14=acd6d06a7ea2efc73a0771530832d77e

WITH cte AS (
SELECT uid
FROM words_social
WHERE social = in_social
AND sid = in_sid
LIMIT 1
)
SELECT
CASE WHEN c.uid = cte.uid THEN 1 ELSE 0 END,
c.msg
FROMwords_chat c
JOINwords_games g USING (gid)
JOINwords_users myself ON (myself.uid IN (g.player1, g.player2)
AND myself.uid = cte.uid)
JOINwords_users opponent ON (opponent.uid IN (g.player1,
g.player2) AND myself.uid <> cte.uid)
JOINcte
WHERE   c.gid= in_gid
AND (c.uid = myself.uid OR NOT opponent.muted)
ORDER BY c.CREATED ASC;

ERROR:  syntax error at or near "WHERE"
LINE 67: WHERE   c.gid= in_gid
 ^

And if I remove the "JOIN cte" line, then the error is:

ERROR:  missing FROM-clause entry for table "cte"
LINE 64: ...elf.uid IN (g.player1, g.player2) AND myself.uid = cte.uid)
   ^

>


Re: Displaying chat by punished users only to themselves (db fiddle attached)

2022-05-04 Thread Alexander Farber
I am probably needing LEFT JOIN LATERAL here (and am completely lost)?

Or to switch to CTE as you suggest


Re: Displaying chat by punished users only to themselves (db fiddle attached)

2022-05-04 Thread Alexander Farber
David, I try then the following -

On Wed, May 4, 2022 at 5:28 PM David G. Johnston 
wrote:

> You missed quoting the part where I describe the on clauses you need to
> distinguish between "them" and "me"
>
> Me: u.uid in (player...) and (s.uid = u.uid)
> Them: u.uid in (player...) and (s.uid <> u.uid)
>
>

https://dbfiddle.uk/?rdbms=postgres_14=048b9b9c7c55256c1a478d7c90cd2667

SELECT
CASE WHEN c.uid = s.uid THEN 1 ELSE 0 END,
c.msg
FROMwords_chat c
JOINwords_games g USING (gid)
JOINwords_users myself ON (myself.uid IN (g.player1, g.player2)
AND myself.uid = s.uid)
JOINwords_users opponent ON (opponent.uid IN (g.player1,
g.player2) AND myself.uid <> s.uid)
JOINwords_social s ON (s.uid = myself.uid)
WHERE   c.gid= in_gid
AND s.social = in_social
AND s.sid= in_sid
AND (c.uid = myself.uid OR NOT opponent.muted)

ORDER BY c.CREATED ASC;

And get the syntax error which don't quite understand:

ERROR:  missing FROM-clause entry for table "s"
LINE 57: ...yself.uid IN (g.player1, g.player2) AND myself.uid = s.uid)
 ^

Probably because "myself" needs "s" and vice versa?


Re: Displaying chat by punished users only to themselves (db fiddle attached)

2022-05-04 Thread Alexander Farber
David, I am trying your suggestion:

On Wed, May 4, 2022 at 4:27 PM David G. Johnston 
wrote:

> Assuming the base query is capable of returning all related chat messages
> for both users (I'd probably place that portion into a CTE) the rows you
> want to filter out are those whose c.uid is not my own, but only if their
> muted property is true.  It makes it easier to understand if you join
> words_users twice, defining one as "them" and one as "me".  Then you can
> say something like:  WHERE (c.uid = me.uid) OR NOT(them.muted)
>
>
like this:


https://dbfiddle.uk/?rdbms=postgres_14=4ab6a09cddae26a11140202fdc41cf5c

CREATE OR REPLACE FUNCTION words_get_chat(
in_gidinteger,
in_social integer,
in_sidtext
) RETURNS TABLE (
out_mine  integer,
out_msg   text
) AS
$func$
SELECT
CASE WHEN c.uid = s.uid THEN 1 ELSE 0 END,
c.msg
FROMwords_chat c
JOINwords_games g USING (gid)
JOINwords_users myself ON (myself.uid IN (g.player1,
g.player2))
JOINwords_users opponent ON (opponent.uid IN (g.player1,
g.player2))
JOINwords_social s ON (s.uid = myself.uid)
WHERE   c.gid= in_gid
AND s.social = in_social
AND s.sid= in_sid
AND (c.uid = myself.uid OR NOT opponent.muted)

ORDER BY c.CREATED ASC;

$func$ LANGUAGE sql;


Re: Displaying chat by punished users only to themselves (db fiddle attached)

2022-05-04 Thread Alexander Farber
My real SQL function has one more param, an "auth" string generated by my
game, which complements the social network id "sid".

I have just omitted it in my test case.

>


Re: Displaying chat by punished users only to themselves (db fiddle attached)

2022-05-04 Thread Alexander Farber
Hi Ron,

On Wed, May 4, 2022 at 4:56 PM Ron  wrote:

>
> How do other web sites know to present only "my" data, even though they
> don't encode "my" user id in the URL?
>
>
that is the usual pattern with OAuth provided by: Facebook, Google, Amazon,
Huawei, etc...

After you auth with them in a game like mine, they give you a social
network id, which is a string. Noone else gets that str.

And then I (as game dev) use that str to id the user and when the user is
visiting my for the 1st time, I give him a numeric id in my game. And an
"auth" str generated by my game. Etc... it works ok.

Regards
Alex


Re: Displaying chat by punished users only to themselves (db fiddle attached)

2022-05-04 Thread Alexander Farber
Thank you for replying, David!

The "social dynamic" is needed, because I cannot pass real user id (via
HTTP) to SQL queries.

Instead I pass social network type "social" (like 100 is facebook, 200 is
twitter) and the social network id "sid" returned by that network. This way
noone can read chats by other users, by just replacing the numeric "uid"...

So I try your suggestion with:

https://dbfiddle.uk/?rdbms=postgres_14=48d4bef569d966021e94c72f86d9fce5

CREATE OR REPLACE FUNCTION words_get_chat(
in_gidinteger,
in_social integer,
in_sidtext
) RETURNS TABLE (
out_mine  integer,
out_msg   text
) AS
$func$
SELECT
CASE WHEN c.uid = s.uid THEN 1 ELSE 0 END,
c.msg
FROMwords_chat c
JOINwords_games g USING (gid)
JOINwords_users u1 ON (u1.uid = g.player1)
JOINwords_users u2 ON (u2.uid = g.player2)
JOINwords_social s ON (s.uid IN (u1.uid, u2.uid))
WHERE   c.gid= in_gid
AND s.social = in_social
AND s.sid= in_sid
ORDER BY c.CREATED ASC;

$func$ LANGUAGE sql;

...but how to bring the u1.muted or u2.muted there?

Best regards
Alex


Displaying chat by punished users only to themselves (db fiddle attached)

2022-05-04 Thread Alexander Farber
Hello,

I have developed a complete SQL fiddle for my question:

https://dbfiddle.uk/?rdbms=postgres_14=dcf063ba1615b392cc3cfa347a32c97b

The context is that I run an online game for two players using a PostgreSQL
14.2 backend.

I would like to make my game more friendly by hiding chat messages of
misbehaving users.

However, to prevent the punished users from noticing it and registering new
game accounts, I would like to still show them all messages :->

So here are the 4 tables used in my reduced test case:

CREATE TABLE words_users (
uid SERIAL PRIMARY KEY,
muted BOOLEAN NOT NULL DEFAULT false
);

CREATE TABLE words_social (
sid text NOT NULL CHECK (sid ~ '\S'),
social  integer  NOT NULL CHECK (0 < social AND social <= 256),
given   text NOT NULL CHECK (given ~ '\S'),
uid integer  NOT NULL REFERENCES words_users ON DELETE CASCADE,
PRIMARY KEY(sid, social)
);

CREATE TABLE words_games (
gid  SERIAL PRIMARY KEY,
player1  integer REFERENCES words_users(uid) ON DELETE CASCADE NOT NULL
CHECK (player1 <> player2),
player2  integer REFERENCES words_users(uid) ON DELETE CASCADE
);

CREATE TABLE words_chat (
cid BIGSERIAL PRIMARY KEY,
created timestamptz NOT NULL,
gid integer NOT NULL REFERENCES words_games ON DELETE CASCADE,
uid integer NOT NULL REFERENCES words_users ON DELETE CASCADE,
msg textNOT NULL
);

Then I put 2 users into the game #10 and they start chatting:

-- create 2 users: one is ok, while the other is muted (punished)
INSERT INTO words_users (uid, muted) VALUES (1, false), (2, true);
INSERT INTO words_social (sid, social, given, uid) VALUES ('abc', 100,
'Nice user', 1), ('def', 200, 'Bad user', 2);

-- put these 2 users into a game #10
INSERT INTO words_games (gid, player1, player2) VALUES (10, 1, 2);

-- both users in the game #10 start chatting
INSERT INTO words_chat (gid, uid, created, msg) VALUES
(10, 1, CURRENT_TIMESTAMP + INTERVAL '1 min', 'Hi how are you doing?'),
(10, 1, CURRENT_TIMESTAMP + INTERVAL '2 min', 'I am a nice user'),
(10, 2, CURRENT_TIMESTAMP + INTERVAL '3 min', 'F*** ***!!'),
(10, 2, CURRENT_TIMESTAMP + INTERVAL '4 min', 'I am a bad user'),
(10, 1, CURRENT_TIMESTAMP + INTERVAL '5 min','Are you there??');

Here is my custom stored function (in SQL, I would prefer not to switch to
PL/pgSQL):

CREATE OR REPLACE FUNCTION words_get_chat(
in_gidinteger,
in_social integer,
in_sidtext
) RETURNS TABLE (
out_mine  integer,
out_msg   text
) AS
$func$
SELECT
CASE WHEN c.uid = s.uid THEN 1 ELSE 0 END,
c.msg
FROMwords_chat c
JOINwords_games g USING (gid)
JOINwords_users u ON (u.uid IN (g.player1, g.player2)
-- The condition below is broken if both users are not muted
AND (u.muted OR (c.uid = u.uid AND NOT u.muted)))
JOINwords_social s ON (s.uid = u.uid)
WHERE   c.gid= in_gid
AND s.social = in_social
AND s.sid= in_sid
ORDER BY c.CREATED ASC;

$func$ LANGUAGE sql;

For a chat of a bad and a nice user it seemingly works:

SELECT words_get_chat(10, 100, 'abc') AS nice_user;
SELECT words_get_chat(10, 200, 'def') AS muted_user;

But if you change both users to be not muted - it will break and they only
will see their own messages.

I have tinkered a lot with my db fiddle... but still cannot figure it out

Thank you!
Alex


Re: if not exists (SELECT 1... UNION SELECT 1...)

2022-02-16 Thread Alexander Farber
s/grumbling/wondering/


Re: if not exists (SELECT 1... UNION SELECT 1...)

2022-02-16 Thread Alexander Farber
Ah, thank you Ralf! That has explained it (because I was still grumbling...)

On Wed, Feb 16, 2022 at 4:44 PM Ralf Schuchardt  wrote:

> You must rather end the assignment with a semicolon:
>
> out_text := '___WRONG___';
> RETURN;
>
> Otherwise what really happens is:
>
> out_text := '___WRONG___' RETURN;
>
> where „return“ is a simple column name as in this select statement:
>
> select '___WRONG___' return;
>
>
>


Re: if not exists (SELECT 1... UNION SELECT 1...)

2022-02-16 Thread Alexander Farber
Ah, I have to do

RETURN '___WRONG___';

and not

out_text := '___WRONG___'
RETURN;


Re: if not exists (SELECT 1... UNION SELECT 1...)

2022-02-16 Thread Alexander Farber
Hi Laurenz, thanks for your reply, but I think it is wrong -

On Wed, Feb 16, 2022 at 1:24 PM Laurenz Albe 
wrote:

> One reason could be index corruption.  If one query uses an index and the
> other doesn't,
> that could lead to different results.
>
> The other option is of course a trivial error, like you are using a
> different search
> string or connect to a different database.
>

if you go to an online PostgreSQL editor like
https://extendsclass.com/postgresql-online.html

and just enter my simple test code below,
you will see that wrongly "__CORRECT__" is printed:

DROP TABLE words_nouns;
DROP TABLE words_verbs;

CREATE TABLE words_nouns (
word text PRIMARY KEY, -- CHECK is added below
hashed text NOT NULL,
expl text
);

CREATE TABLE words_verbs (
word text PRIMARY KEY, -- CHECK is added below
hashed text NOT NULL
);

ALTER TABLE words_nouns
ADD CONSTRAINT words_nouns_word_check
CHECK (
word ~ '^[А-Я]{2,}$' AND
word !~ '[ЖШ]Ы' AND
word !~ '[ЧЩ]Я'
);

ALTER TABLE words_verbs
ADD CONSTRAINT words_verbs_word_check
CHECK (
word ~ '^[А-Я]{2,}$' AND
word !~ '[ЖШ]Ы' AND
word !~ '[ЧЩ]Я' AND
word !~ 'Ц[ЮЯ]' AND
(word ~ '[ТЧ]ЬСЯ$' OR
 word ~ '[ТЧ]Ь$' OR
 word ~ 'ТИ$')
);

CREATE OR REPLACE FUNCTION words_guess_puzzle(
in_mid   bigint,
in_guess text,
OUT out_text text
) RETURNS text AS
$func$
BEGIN
in_guess := UPPER(in_guess);

-- check if the in_guess is a valid word - WHY DOES THIS NEVER TRIGGER?
IF NOT EXISTS(SELECT 1 FROM words_nouns WHERE word = in_guess UNION
  SELECT 1 FROM words_verbs WHERE word = in_guess) THEN
out_text := '___WRONG___'
RETURN;
END IF;

out_text := '___CORRECT___'
RETURN;
END
$func$ LANGUAGE plpgsql;

SELECT words_guess_puzzle(123, 'ABCDE');


if not exists (SELECT 1... UNION SELECT 1...)

2022-02-16 Thread Alexander Farber
Hello,

when I search for a non existent word in the two tables hosted in
PostgreSQL 14.1 then I get zero records as expected:

words_en=> SELECT 1 FROM words_nouns WHERE word = 'ABCDE' UNION SELECT 1
FROM words_verbs WHERE word = 'ABCDE';
 ?column?
--
(0 rows)

But when I try to use the same command in my stored function, then it goes
through, as if the word would exist (and a new record is inserted into the
words_puzzle table):

CREATE OR REPLACE FUNCTION words_guess_puzzle(
in_mid   bigint,
in_socialinteger,
in_sid   text,
in_auth  text,
in_guess text,
OUT out_text text
) RETURNS text AS
$func$
DECLARE
_uid integer;
BEGIN
IF NOT words_valid_user(in_social, in_sid, in_auth) THEN
RAISE EXCEPTION 'Invalid user = % %', in_social, in_sid;
END IF;

_uid := (SELECT uid FROM words_social WHERE social = in_social AND sid
= in_sid);

in_guess := UPPER(in_guess);

-- check if the in_guess is a valid word - WHY DOES THIS NEVER TRIGGER?
IF NOT EXISTS(SELECT 1 FROM words_nouns WHERE word = in_guess UNION
  SELECT 1 FROM words_verbs WHERE word = in_guess) THEN
out_text := '___WRONG___'
RETURN;
END IF;

INSERT INTO words_puzzle (mid, uid, word, guessed)
VALUES (in_mid, _uid, in_guess, CURRENT_TIMESTAMP);

out_text := '___CORRECT___'
RETURN;
END
$func$ LANGUAGE plpgsql;

What could be the reason please?

Best regards
Alex


Re: [jetty-users] Migrating 9 to 10: what replaces WebSocketServlet and JSON.parse() ?

2021-09-04 Thread Alexander Farber
Apologies for having asked such simple question, I have only some
experience with embedded Java...

I have added the


org.eclipse.jetty
jetty-slf4j-impl
${jetty.version}


to the pom.xml and now my WAR servlet finally works well with Jetty 10.0.6.

Does it make any sense to add "servlet" module, when I already have
"deploy" module added?

I have tried with and without "--add-module=servlet" and could not see any
difference.

Also, I am curious that when I compile my WAR file against Jetty 10.0.6 and
then put it into webapps dir of the old Jetty 9.4.x installation on my
Linux server, then it fails with:

Sep 04 10:38:28 afarber.de java[206224]: 2021-09-04
10:38:28.833:WARN:oejw.WebAppContext:main: Failed startup of context
o.e.j.w.WebAppContext@3f56875e
{/en,file:///tmp/jetty-127_0_0_1-8082-words-4_0_war-_en-any-14171957377492967902
/webapp/,UNAVAILABLE}{/var/www/words-4.0.war}
Sep 04 10:38:28 afarber.de java[206224]: java.lang.RuntimeException:
java.lang.IllegalStateException: No Jetty ContextHandler, Jetty WebSocket
SCI unavailable
Sep 04 10:38:28 afarber.de java[206224]: at
org.eclipse.jetty.annotations.ServletContainerInitializersStarter.doStart(ServletContainerInitializersStarter.java:69)
Sep 04 10:38:28 afarber.de java[206224]: at
org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:73)

Shouldn't the WAR file have some standard APIs and thus run fine in both
deployments, Jetty 10 and Jetty 9?

Best regards
Alex
___
jetty-users mailing list
jetty-users@eclipse.org
To unsubscribe from this list, visit 
https://www.eclipse.org/mailman/listinfo/jetty-users


Re: [jetty-users] Migrating 9 to 10: what replaces WebSocketServlet and JSON.parse() ?

2021-09-03 Thread Alexander Farber
Thank you, Joakim, now my servlet runs, but -

On Fri, Sep 3, 2021 at 6:32 PM Joakim Erdfelt  wrote:

> You are using a JettyWebSocketCreator.
>
> Get rid of  factory.register(WordsListener.class);
>

Jetty 10 prints:

SLF4J: No SLF4J providers were found.
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#noProviders for further details.

And then I don't see any logs printed by my servlet with

Logger LOG = Log.getLogger(LOG_TAG);

LOG.info("");

And thus I cannot find out why there is no data returned from PostgreSQL...

Maybe this line does not really work?

factory.setCreator(new WordsCreator(this,
mBundle.getString(STR_DATABASE_URL)));

My servlet is multilanguage and thus I was passing differen DB credentials
with the line above.

What should I please use instead instead of Log.getLogger()? There
is @deprecated, but no replacement suggestion.

Best regards
Alex



> On Fri, Sep 3, 2021 at 11:09 AM Alexander Farber <
> alexander.far...@gmail.com> wrote:
>
>> Hi, thank you for the helpful hints, now my WAR compiles.
>>
>> However when I launch it by:
>>
>> 1) First running once on Win 10 / Java 11:
>>
>> java -jar jetty-home-10.0.6\start.jar jetty.home=jetty-home-10.0.6
>> jetty.base=jetty.base
>> --add-module=http,servlet,webapp,deploy,resources,websocket
>>
>> 2) Then running
>>
>> @SET JETTY_HOME=%~dp0\jetty-home-10.0.6
>> @SET JETTY_BASE=%~dp0\jetty.base
>> java -Djdbc.drivers=org.postgresql.Driver -jar %JETTY_HOME%\start.jar
>> jetty.home=%JETTY_HOME% jetty.base=%JETTY_BASE%
>>
>> Then Jetty 10 starts fine initially:
>>
>> 2021-09-03 17:59:51.475:INFO :oejsh.ContextHandler:main: Started
>> o.e.j.w.WebAppContext@42a9e5d1
>> {/en,file:///C:/Users/U299FAV/src/slova/webapp/target/words-4.0/,AVAILABLE}{C:\Users\U299FAV\src\slova\jetty.base/../webapp/target/words-4.0.war}
>> 2021-09-03 17:59:51.485:INFO :oejsh.ContextHandler:main: Started
>> o.e.j.s.h.ContextHandler@2c1b9e4b{/,null,AVAILABLE}
>> 2021-09-03 17:59:51.507:INFO :oejs.AbstractConnector:main: Started
>> ServerConnector@7e027a05{HTTP/1.1, (http/1.1)}{0.0.0.0:8080}
>> 2021-09-03 17:59:51.518:INFO :oejs.Server:main: Started 
>> Server@624ea235{STARTING}[10.0.6,sto=5000]
>> @4326ms
>>
>> But when I try accessing the following path (I put the XML file under
>> jetty.base\webapps):
>>
>> 
>> > "http://www.eclipse.org/jetty/configure_9_0.dtd;>
>> 
>> /ru
>> > name="jetty.base"/>/../webapp/target/words-4.0.war
>> 
>>
>> Then Jetty 10 reports the error:
>>
>> 2021-09-03 18:00:00.583:WARN :oejshC.ru:qtp1072410641-16: unavailable
>> java.lang.RuntimeException: java.lang.NoSuchMethodException:
>> de.afarber.WordsListener.()
>> at
>> org.eclipse.jetty.websocket.server.JettyWebSocketServlet$CustomizedWebSocketServletFactory.register(JettyWebSocketServlet.java:218)
>> at de.afarber.WordsServlet.configure(WordsServlet.java:646)
>> at
>> org.eclipse.jetty.websocket.server.JettyWebSocketServlet.init(JettyWebSocketServlet.java:168)
>> at de.afarber.WordsServlet.init(WordsServlet.java:652)
>> at javax.servlet.GenericServlet.init(GenericServlet.java:180)
>> at
>> org.eclipse.jetty.servlet.ServletHolder$Wrapper.init(ServletHolder.java:1305)
>> at
>> org.eclipse.jetty.servlet.ServletHolder.initServlet(ServletHolder.java:633)
>> at
>> org.eclipse.jetty.servlet.ServletHolder.getServlet(ServletHolder.java:486)
>> at
>> org.eclipse.jetty.servlet.ServletHolder.prepare(ServletHolder.java:731)
>> at
>> org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:503)
>> at
>> org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:131)
>> at
>> org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:580)
>> at
>> org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)
>> at
>> org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:223)
>> at
>> org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1571)
>> at
>> org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:221)
>> at
>> org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1372)
>> at
>> org.eclipse.jetty.server.hand

Re: [jetty-users] Migrating 9 to 10: what replaces WebSocketServlet and JSON.parse() ?

2021-09-03 Thread Alexander Farber
Hi, thank you for the helpful hints, now my WAR compiles.

However when I launch it by:

1) First running once on Win 10 / Java 11:

java -jar jetty-home-10.0.6\start.jar jetty.home=jetty-home-10.0.6
jetty.base=jetty.base
--add-module=http,servlet,webapp,deploy,resources,websocket

2) Then running

@SET JETTY_HOME=%~dp0\jetty-home-10.0.6
@SET JETTY_BASE=%~dp0\jetty.base
java -Djdbc.drivers=org.postgresql.Driver -jar %JETTY_HOME%\start.jar
jetty.home=%JETTY_HOME% jetty.base=%JETTY_BASE%

Then Jetty 10 starts fine initially:

2021-09-03 17:59:51.475:INFO :oejsh.ContextHandler:main: Started
o.e.j.w.WebAppContext@42a9e5d1
{/en,file:///C:/Users/U299FAV/src/slova/webapp/target/words-4.0/,AVAILABLE}{C:\Users\U299FAV\src\slova\jetty.base/../webapp/target/words-4.0.war}
2021-09-03 17:59:51.485:INFO :oejsh.ContextHandler:main: Started
o.e.j.s.h.ContextHandler@2c1b9e4b{/,null,AVAILABLE}
2021-09-03 17:59:51.507:INFO :oejs.AbstractConnector:main: Started
ServerConnector@7e027a05{HTTP/1.1, (http/1.1)}{0.0.0.0:8080}
2021-09-03 17:59:51.518:INFO :oejs.Server:main: Started
Server@624ea235{STARTING}[10.0.6,sto=5000]
@4326ms

But when I try accessing the following path (I put the XML file under
jetty.base\webapps):


http://www.eclipse.org/jetty/configure_9_0.dtd;>

/ru
/../webapp/target/words-4.0.war


Then Jetty 10 reports the error:

2021-09-03 18:00:00.583:WARN :oejshC.ru:qtp1072410641-16: unavailable
java.lang.RuntimeException: java.lang.NoSuchMethodException:
de.afarber.WordsListener.()
at
org.eclipse.jetty.websocket.server.JettyWebSocketServlet$CustomizedWebSocketServletFactory.register(JettyWebSocketServlet.java:218)
at de.afarber.WordsServlet.configure(WordsServlet.java:646)
at
org.eclipse.jetty.websocket.server.JettyWebSocketServlet.init(JettyWebSocketServlet.java:168)
at de.afarber.WordsServlet.init(WordsServlet.java:652)
at javax.servlet.GenericServlet.init(GenericServlet.java:180)
at
org.eclipse.jetty.servlet.ServletHolder$Wrapper.init(ServletHolder.java:1305)
at
org.eclipse.jetty.servlet.ServletHolder.initServlet(ServletHolder.java:633)
at
org.eclipse.jetty.servlet.ServletHolder.getServlet(ServletHolder.java:486)
at
org.eclipse.jetty.servlet.ServletHolder.prepare(ServletHolder.java:731)
at
org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:503)
at
org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:131)
at
org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:580)
at
org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)
at
org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:223)
at
org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1571)
at
org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:221)
at
org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1372)
at
org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:176)
at
org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:463)
at
org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1544)
at
org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:174)
at
org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1294)
at
org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:129)
at
org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:192)
at
org.eclipse.jetty.server.handler.HandlerList.handle(HandlerList.java:51)
at
org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)
at org.eclipse.jetty.server.Server.handle(Server.java:562)
at
org.eclipse.jetty.server.HttpChannel.lambda$handle$0(HttpChannel.java:406)
at
org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:663)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:398)
at
org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:282)
at
org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:319)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:100)
at
org.eclipse.jetty.io.SocketChannelEndPoint$1.run(SocketChannelEndPoint.java:101)
at
org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:894)
at
org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1038)
at java.base/java.lang.Thread.run(Thread.java:834)

My WordsListener.java looks like:

import org.eclipse.jetty.util.ajax.JSON;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketListener;
import 

Re: [jetty-users] Migrating 9 to 10: what replaces WebSocketServlet and JSON.parse() ?

2021-09-01 Thread Alexander Farber
Hi Joakim and all -

On Tue, Aug 31, 2021 at 6:19 PM Joakim Erdfelt  wrote:

> With that said, you appear to be using the Jetty implementation/apis.
> So, switch to `org.eclipse.jetty.websocket.server.JettyWebSocketServlet`
> instead.
>


> JSON json = new JSON();
> // configure json here
> Map myMap = (Map) json.fromJSON(str);
>

thank you for the helpful tips on JSON parsing.

Could you please help me with creating JSON?

In Jetty 9.4.x I had this method:

// this string is POSTed to Google FCM server
public String toFcmBody(String boardUrl, String gameNumber) {
Map root  = new HashMap<>();
Map notif = new HashMap<>();
Map data  = new HashMap<>();
root.put(KEY_TO, mFcm);
root.put(KEY_NOTIFICATION, notif);
root.put(KEY_DATA, data);
notif.put(KEY_TITLE, gameNumber + " " + mGid);
notif.put(KEY_BODY, mBody);
notif.put(KEY_IMAGE, boardUrl + mGid);
data.put(KEY_GID, mGid);
return JSON.toString(root);
}

So now I am trying the following, but what to use as the first param to
appendMap()?

// this string is POSTed to Google FCM server
public String toFcmBody(String boardUrl, String gameNumber) {
Map root  = new HashMap<>();
Map notif = new HashMap<>();
Map data  = new HashMap<>();
root.put(KEY_TO, mFcm);
root.put(KEY_NOTIFICATION, notif);
root.put(KEY_DATA, data);
notif.put(KEY_TITLE, gameNumber + " " + mGid);
notif.put(KEY_BODY, mBody);
notif.put(KEY_IMAGE, boardUrl + mGid);
data.put(KEY_GID, mGid);
// new code
JSON json = new JSON();
json.appendMap(???, root);
return json.toString();
}

And also Jetty 10 is not happy with the deprecated header() method here:

MultiMap postParams = new MultiMap<>();
postParams.put("code",  code);
postParams.put("client_id",
 mBundle.getString(STR_GOOGLE_ID));
postParams.put("client_secret",
mBundle.getString(STR_GOOGLE_SECRET));
postParams.put("redirect_uri",
String.format(GOOGLE_REDIRECT_URI, mLanguage));
postParams.put("grant_type","authorization_code");

String tokenStr = mHttpClient.POST(GOOGLE_TOKEN_URL)
.header(HttpHeader.ACCEPT, APPLICATION_JSON)
   // what to use here please?
.header(HttpHeader.CONTENT_TYPE, APPLICATION_URLENCODED)
.content(new
StringContentProvider(UrlEncoded.encode(postParams, StandardCharsets.UTF_8,
false)))
.send().getContentAsString();
Map tokenMap = (Map)
mJson.fromJSON(tokenStr);

Best regards
Alex
___
jetty-users mailing list
jetty-users@eclipse.org
To unsubscribe from this list, visit 
https://www.eclipse.org/mailman/listinfo/jetty-users


[jetty-users] Migrating 9 to 10: what replaces WebSocketServlet and JSON.parse() ?

2021-08-31 Thread Alexander Farber
Hello,

I am trying to migrate a war servlet from 9.4.43.v20210629 to 10.0.6 and
have read
https://www.eclipse.org/jetty/documentation/jetty-10/programming-guide/index.html#pg-migration-94-to-10

But unfortunately I still have questions - in my servlet I have:

public class MyServlet extends WebSocketServlet {
@Override
public void configure(WebSocketServletFactory factory) {
mLanguage = System.getenv("COUNTRY");
mBundle = ResourceBundle.getBundle("strings",
LOCALES.get(mLanguage));
factory.getPolicy().setIdleTimeout(IDLE_TIMEOUT_SECONDS * 1000);
factory.getPolicy().setMaxBinaryMessageSize(0);
factory.getPolicy().setMaxTextMessageSize(64 * 1024);
factory.register(MyListener.class);
factory.setCreator(new MyCreator(this,
mBundle.getString(STR_DATABASE_URL)));
}
@Override
protected void doGet(HttpServletRequest httpReq, HttpServletResponse
httpResp) throws ServletException, IOException {
   // 
}
}

What is the class to use in Jetty 10 instead of WebSocketServlet?

Also, I use the following code in few spots to parse JSON strings:

Map myMap = (Map) JSON.parse(str);

but now I get the compile error: String cannot be converted to Source

Is there a new parser, should I maybe use AsyncJSON and how?

Best regards
Alex
___
jetty-users mailing list
jetty-users@eclipse.org
To unsubscribe from this list, visit 
https://www.eclipse.org/mailman/listinfo/jetty-users


Re: [jetty-users] Using WebAppContext at contextPath / but also serving images, sounds folders

2021-08-10 Thread Alexander Farber
On Tue, Aug 10, 2021 at 12:42 PM  wrote:

> https://stackoverflow.com/a/68682598/458157
>
>
>
Ah the longest match is used? I was assuming the folder order in the config
files and maybe thus I was having problems
___
jetty-users mailing list
jetty-users@eclipse.org
To unsubscribe from this list, visit 
https://www.eclipse.org/mailman/listinfo/jetty-users


Re: [jetty-users] Using WebAppContext at contextPath / but also serving images, sounds folders

2021-08-10 Thread Alexander Farber
Yes, Jan -

On Tue, Aug 10, 2021 at 4:20 AM Jan Bartel  wrote:

> I'm not sure what you are wanting to do?  Do you want to keep your 2
> static content ContextHandlers (at context paths "/images" and "/sounds")
> and your var/www/myservlet-3.0.war but move it to context path "/"?  Or are
> you asking can you combine all 3 into a single webapp?
>

the first option, how to do it please?

Sorry if I am asking something basic, I am not experienced with using
servlets

Best regards
Alex



> On Mon, 9 Aug 2021 at 21:21, Alexander Farber 
> wrote:
>
>>
>> I am using 9.4.43.v20210629 for two things:
>>
>> 1) serve Websockets and GET, POST requests
>> 2) serve files located in "images" and "sounds" folders
>>
>> My config for 1 is currently using contextPath "/ws":
>>
>> 
>> > "http://www.eclipse.org/jetty/configure_9_0.dtd;>
>> 
>>
>> /ws
>> /var/www/myservlet-3.0.war
>>
>> 
>>
>>
>> And the two configs for 2 are (only the "resourceBase" differs):
>>
>> 
>> > "http://www.eclipse.org/jetty/configure_9_0.dtd;>
>> 
>> /images
>> 
>> 
>>  /var/www/html/mydomain.com/images
>> 
>> true
>> 
>> 
>> 
>>
>>
>> It works well for several years already (and behind HAProxy 1.8.27), but
>> I am not happy with having  contextPath "/ws" as a prefix to my servlet.
>>
>> I would like to change it to "/" aka root, but wonder how to keep
>> "/images" and "/sounds" also servable by Jetty.
>>
>> Would org.eclipse.jetty.fcgi.server.proxy.TryFilesFilter help me in this
>> situation?
>>
>> If a file (a sound or image file) is found, it should be served.
>> Otherwise the Websockets, GET or POST request should be forwarded to my
>> servlet.
>>
>> In the Jetty doc for FastCGI there is an example:
>>
>> 
>> org.eclipse.jetty.fcgi.server.proxy.TryFilesFilter
>> /*
>> 
>> 
>> > class="javax.servlet.DispatcherType" />
>> 
>> 
>> 
>> files
>> $path /index.php?p=$path
>> 
>> 
>>
>> but I am not sure how to apply it to my situation, how to replace the
>> "index.php" by my servlet there?
>>
>>
___
jetty-users mailing list
jetty-users@eclipse.org
To unsubscribe from this list, visit 
https://www.eclipse.org/mailman/listinfo/jetty-users


[jetty-users] Using WebAppContext at contextPath / but also serving images, sounds folders

2021-08-09 Thread Alexander Farber
Hello dear Jetty users and developers,

I am using 9.4.43.v20210629 for two things:

1) serve Websockets and GET, POST requests
2) serve files located in "images" and "sounds" folders

My config for 1 is currently using contextPath "/ws":


http://www.eclipse.org/jetty/configure_9_0.dtd;>


/ws
/var/www/myservlet-3.0.war




And the two configs for 2 are (only the "resourceBase" differs):


http://www.eclipse.org/jetty/configure_9_0.dtd;>

/images


 /var/www/html/mydomain.com/images

true





It works well for several years already (and behind HAProxy 1.8.27), but I
am not happy with having  contextPath "/ws" as a prefix to my servlet.

I would like to change it to "/" aka root, but wonder how to keep "/images"
and "/sounds" also servable by Jetty.

Would org.eclipse.jetty.fcgi.server.proxy.TryFilesFilter help me in this
situation?

If a file (a sound or image file) is found, it should be served. Otherwise
the Websockets, GET or POST request should be forwarded to my servlet.

In the Jetty doc for FastCGI there is an example:


org.eclipse.jetty.fcgi.server.proxy.TryFilesFilter
/*






files
$path /index.php?p=$path



but I am not sure how to apply it to my situation, how to replace the
"index.php" by my servlet there?

Best regards
Alex
___
jetty-users mailing list
jetty-users@eclipse.org
To unsubscribe from this list, visit 
https://www.eclipse.org/mailman/listinfo/jetty-users


Re: Stored function RETURNS table, but in some cases columns are missing - should I set them to NULL?

2021-03-09 Thread Alexander Farber
Tom, you are so eagle eyed -

On Mon, Mar 8, 2021 at 8:53 PM Tom Lane  wrote:

> Alexander Farber  writes:
> > However there are cases, when I only have the out_gid value, I do not
> want
> > to return any other values.
> > My question is: do I have to set the other OUT params explicitly to NULL?
>
> plpgsql initializes them to null by default, I believe, just like ordinary
> local variables.
>
> > org.postgresql.util.PSQLException: ERROR: column "out_uid" does not
> exist|
>
> This isn't related to what your function does internally.
>
> I think the issue is you renamed out_uid to uid in your SELECT:
>
> >String SQL_JOIN_GAME   =
> >"SELECT " +
> >"out_uidAS uid," +
>
>
thank you and sorry for my silly mistake


Stored function RETURNS table, but in some cases columns are missing - should I set them to NULL?

2021-03-08 Thread Alexander Farber
Good evening,

in PostgreSQL 13.2 I have a custom stored function:

CREATE OR REPLACE FUNCTION words_join_new_game(
in_uid   integer,
in_bid   integer
) RETURNS table (
-- the player to be notified (sometimes there is no such
user)
out_uidinteger,
-- the id of the created game
out_gidinteger,
out_fcmtext,
out_apns   text,
out_admtext,
out_hmstext,
-- the most recently used social network and the user id
there
out_social integer,
out_sidtext,
-- the push notification text: the opponent has joined
out_body   text
) AS
$func$

$func$ LANGUAGE plpgsql;

However there are cases, when I only have the out_gid value, I do not want
to return any other values.

My question is: do I have to set the other OUT params explicitly to NULL?

For example here:

-- case 1
SELECT gid
INTO   out_gid
FROM   words_games
WHERE  finished IS NULL
ANDbid = in_bid
AND(
(player1 = in_uid AND played1 IS NULL) OR
(player2 = in_uid AND played2 IS NULL)
) LIMIT 1;

IF out_gid IS NOT NULL THEN
-- should I set all the other OUT params to NULL here?
<---
RETURN;
END IF;

I was expecting to check for out_uid, if it is a positive number in my Java
code with:

String SQL_JOIN_GAME   =
"SELECT " +
"out_uidAS uid," +
// the id of the new game is never NULL, but the other
columns can be NULL
"out_gidAS gid," +
"out_fcmAS fcm," +
"out_apns   AS apns,   " +
"out_admAS adm," +
"out_hmsAS hms," +
"out_social AS social, " +
"out_sidAS sid," +
"out_body   AS body" +
"FROM words_join_new_game(?::int, ?::int)";

int gid = 0;
try (Connection db = DriverManager.getConnection(mDatabaseUrl);
PreparedStatement st = db.prepareStatement(SQL_JOIN_GAME)) {
st.setInt(1, mUid);
st.setInt(2, bid);
ResultSet rs = st.executeQuery();
if (rs.next()) {
// get the id of the new game
gid = rs.getInt(KEY_GID);
// get the id of the opponent
int uid = rs.getInt(KEY_UID);
// send notification to the other player
if (uid > 0) {
Notification n = new Notification(
uid,
gid,
rs.getString(COLUMN_FCM),
rs.getString(COLUMN_APNS),
rs.getString(COLUMN_ADM),
rs.getString(COLUMN_HMS),
rs.getInt(COLUMN_SOCIAL),
rs.getString(COLUMN_SID),
rs.getString(COLUMN_BODY)
);
mServlet.sendNotification(n);
}
}
}

but I am getting the error:

org.postgresql.util.PSQLException: ERROR: column "out_uid" does not exist|
Position: 8
at
org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2553)
at
org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2285)
at
org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:323)
at
org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:481)
at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:401)
at
org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:164)
at
org.postgresql.jdbc.PgPreparedStatement.executeQuery(PgPreparedStatement.java:114)
at de.afarber.WordsListener.handleNewGame(WordsListener.java:216)
at de.afarber.WordsListener.onWebSocketText(WordsListener.java:101)

Thank you
Alex


Re: Localizing stored functions by replacing placeholders in their body

2021-03-03 Thread Alexander Farber
Thanks for your input

Actually, yes, that is what I have right now a translate() like stored
function, with format %s sometimes.

But that is "at runtime" and I would like to have a "at compile
time"/"deploy once and forget" solution, that is why I have asked about
approaches for modifying the bodys of my stored functions.

After some more thinking yesterday I have decided to embed the SQL files
with stored functions declarations in my servlet JAR file - that is where
the rest and most of my translations are.

I will just load them from servlet resources when the servlet start,
replace the placeholder by Java and the execute them with CREATE OR REPLACE
FUNCTION ...


Re: Localizing stored functions by replacing placeholders in their body

2021-03-02 Thread Alexander Farber
I think I will try this approach:

 \set localized_declaration `sed 's/this/that/' my_func.sql`
 :localized_declaration

Thank you for your input


Re: Localizing stored functions by replacing placeholders in their body

2021-03-02 Thread Alexander Farber
Ah, I understand, that was the wrong EXECUTE, thank you.

Another idea: can't I use \set command for my purpose of localizing stored
functions?

\set my_func_declaration `sed 's/this/that/' my_func.sql`

But how to execute the declaration? I can only echo it with

select (:'my_func_declaration');


Re: Localizing stored functions by replacing placeholders in their body

2021-03-02 Thread Alexander Farber
Thank you for the \! hint, Pavel, didn't know about that!

Is it possible to have a pure SQL solution? (To avoid having to install
"sed" on my Win 10 PC)

Maybe by using EXECUTE?

EXECUTE REGEXP_REPLACE(
$localize$

CREATE OR REPLACE FUNCTION my_func()
RETURNS text AS
$func$
SELECT '$(placeholder)';
$func$ LANGUAGE sql IMMUTABLE;

$localize$, '\$\(\w+\)', 'English word', 'g');

Unfortunately, I get the error:

ERROR:  prepared statement "regexp_replace" does not exist


Re: Localizing stored functions by replacing placeholders in their body

2021-03-02 Thread Alexander Farber
Or is it possible to call external commands from an sql script, like

\i "sed 's/this/that/' some.sql"


Re: Localizing stored functions by replacing placeholders in their body

2021-03-02 Thread Alexander Farber
Yes, good point about the '\$', thank you Tom.

The reason I am trying not to use sed, is because I deploy my database by
executing a single command:

psql words_en < words_en.sql

And the file words_en.sql has the contents:

\i words_hash.sql
\i words_all_letters.sql
\i words_get_hint.sql
\i words_get_notification.sql
\i ../words_common.sql
\i words_valid_tile.sql
\i words_get_moves.sql
\i words_answer_puzzle.sql
\i words_rare_letter_1.sql
\i words_rare_letter_2.sql

And then the ../words_common.sql creates tables and has 40 more "\i" calls.

So I was hoping to have some SQL command to localize my stored functions.

Best regards
Alex


Localizing stored functions by replacing placeholders in their body

2021-03-02 Thread Alexander Farber
Hello,

I have an app using PostgreSQL 13.2, in 6 different human languages (each
using different database, but same source code).

Currently to localize strings return/set by the stored functions I either
get localized strings from a table or maintain stored function source code
in 6 different languages.

This is not very comfortable and I would like to switch to using same
source code (regardless of the human language) for all stored functions.
And after deploying a database, just run few commands to replace
placeholders in the stored functions.

So I am trying:

CREATE OR REPLACE FUNCTION localize_hello()
RETURNS text AS
$func$
SELECT '$(hello)';
$func$ LANGUAGE sql IMMUTABLE;

And then:

update pg_proc set prosrc = regexp_replace(prosrc, '$\(\w+\)','Hi
english','g') where proname='localize_hello';

But the error is:

ERROR:  permission denied for table pg_proc

So I connect as user "postgres" and then the command seemingly succeeds,
but when I call it, the delivered string is still old:

 select * from localize_hello();
 localize_hello

 $(hello)
(1 row)

Is this a right approach? Do you please have any advice here?

Thanks
Alex


Re: [jetty-users] Is static ConcurrentHashMap a reliable choice for WebSocketServlet?

2021-03-01 Thread Alexander Farber
Hi Lachlan, thank you for your comments!

Over the weekend I have rewritten the custom WebSocketListener in my little
word game to have a static ConcurrentHashMap of ConcurrentHashMaps with the
outer key being user ids and with the inner keys being strings made
of session.getRemoteAddress().getHostString() + ":" +
session.getRemoteAddress().getPort() - because they identify the multiple
Sessions for a user (when she/he uses several browser tabs or maybe uses
desktop and mobile versions at the same time):

https://gist.github.com/afarber/4f82205881ddb0223130f74b4e87abda

And yes, I had also the structure in the servlet before and passed it by a
custom WebSocketCreator. Both variants work and of course Jetty has only 1
process (I can see it on my Linux machine).

At the same I have followed Joakim's advice and decreased the idle timeout
to 5 minutes.

And additionally I track when there is no human actions at the remote end
for 5 minutes - and close those sessions at my servlet (to handle the
abandoned browser tabs). I didn't have to use ping in my code...

I have impression that Jetty source code is very stable and overall
excellent, thank you

Sorry if my mail was a bit offtopic

Best regards
Alex
___
jetty-users mailing list
jetty-users@eclipse.org
To unsubscribe from this list, visit 
https://www.eclipse.org/mailman/listinfo/jetty-users


[jetty-users] Is static ConcurrentHashMap a reliable choice for WebSocketServlet?

2021-02-27 Thread Alexander Farber
Good evening,

In a custom WebSocketServlet in Jetty 9.4.37.v20210219 I would like to
maintain Session objects in a shared data structure.

Is a static data structure like

public final static Map SESSIONS = new
ConcurrentHashMap<>();

a good choice for that?

I have a feeling it does not work reliably. Maybe Jetty starts several
Linux process and thus the static data structure is not shared among them?

Because in my custom WebSocketListener I have a code:

@Override
public void onWebSocketText(String str) {
// here the user is authenticated and mUid is found
Session oldSession = SESSIONS.put(mUid, mSession);
disconnect(oldSession);
}

private void disconnect(Session session) {
LOG.info("disconnect: session={}", session); // surprisingly often
session is null
try {
session.close();
session.disconnect();
} catch (Exception ex) {
// ignore
}
}

And often the old session printed by the above LOG is null, even though I
would expect it be non-null.

Best regards
Alex
___
jetty-users mailing list
jetty-users@eclipse.org
To unsubscribe from this list, visit 
https://www.eclipse.org/mailman/listinfo/jetty-users


Re: [jetty-users] WebSocketListener.onWebSocketClose called numerous times

2021-02-27 Thread Alexander Farber
On the browser side I had automatic reconnects implemented, whenever the
Websockets connection was closed.

But now I am trying to detect, that a player is already connected in
another browser tab and then send a custom close status 4000 from Jetty (so
that automatic reconnect is disabled there and a dialog is displayed to the
user, offering to reconnect):

// called by the onWebSocketText Jetty-callback
private void handleLogin(String username, String password) throws
Exception {
// check username + password and then send the player her/his open
games
mSession.getRemote().sendStringByFuture(mServlet.getGames(mUid));

// if oldSession is not null and still open,
// then the player has several browser tabs open or
// uses the mobile and the desktop app at the same time
Session oldSession = SESSIONS.put(mUid, mSession);
try {
oldSession.close(4000, "another game session detected");
oldSession.disconnect();
} catch (Exception ex) {
// ignore
}

However this only works sporadically, I rarely see the code 4000 in the
browser.

Is this caused by the half open connections too?

Thank you
Alex
___
jetty-users mailing list
jetty-users@eclipse.org
To unsubscribe from this list, visit 
https://www.eclipse.org/mailman/listinfo/jetty-users


Re: [jetty-users] WebSocketListener.onWebSocketClose called numerous times

2021-02-27 Thread Alexander Farber
Hi Joakim, I have changed my custom 9.4.37.v20210219 WebSocketServlet to

@Override
public void configure(WebSocketServletFactory factory) {
factory.getPolicy().setIdleTimeout(5 * 60 * 1000);
factory.getPolicy().setMaxBinaryMessageSize(0);
factory.getPolicy().setMaxTextMessageSize(64 * 1024);
factory.register(MyListener.class);
factory.setCreator(new MyCreator(this)));
}

and have started implementing calling sendPing() every 4 minutes for the
connected clients, but then I have read your other message at
https://stackoverflow.com/a/54654700/165071

"Setting Max Idle Timeout and then causing the connection to not be idle by
sending ping/pong isn't ideal."

Do you mean by that, that if my custom WebSocketServlet will send PING
every 4 minutes to the browser clients, then the connection will never be
idle? Or what do you mean by "isn't ideal"? What else could be done here?

Thank you
Alex
___
jetty-users mailing list
jetty-users@eclipse.org
To unsubscribe from this list, visit 
https://www.eclipse.org/mailman/listinfo/jetty-users


Re: [jetty-users] WebSocketListener.onWebSocketClose called numerous times

2021-02-27 Thread Alexander Farber
Thank you, Joakim!
___
jetty-users mailing list
jetty-users@eclipse.org
To unsubscribe from this list, visit 
https://www.eclipse.org/mailman/listinfo/jetty-users


Re: [jetty-users] WebSocketListener.onWebSocketClose called numerous times

2021-02-26 Thread Alexander Farber
Thank you for your suggestion, Joakim, I didn't think about it.

So I have added the remote port to the log (and also upgraded to Jetty
9.4.37.v20210219)

and yes - the ports are different (please see the log below)... but how can
a single user have so many connections (over a dozen) open to my game which
requires a single connection only?

What is the explanation? Some buggy browser (my players are from Russia) or
too many tabs?

qtp100555887-17: onWebSocketClose: hostname=77.111.247.123 port=13533
mUid=18555 statusCode=1006 reason=Disconnected
qtp100555887-34: onWebSocketClose: hostname=77.111.247.123 port=55271
mUid=18555 statusCode=1006 reason=Disconnected
qtp100555887-71: onWebSocketClose: hostname=77.111.247.123 port=33979
mUid=18555 statusCode=1006 reason=Disconnected
qtp100555887-17: onWebSocketClose: hostname=77.111.247.123 port=58969
mUid=0 statusCode=1006 reason=Disconnected
qtp100555887-54: onWebSocketClose: hostname=77.111.247.123 port=56049
mUid=18555 statusCode=1006 reason=Disconnected
qtp100555887-34: onWebSocketClose: hostname=77.111.247.123 port=37309
mUid=0 statusCode=1006 reason=Disconnected
qtp100555887-54: onWebSocketClose: hostname=77.111.247.123 port=52163
mUid=18555 statusCode=1006 reason=Disconnected
qtp100555887-17: onWebSocketClose: hostname=77.111.247.123 port=59447
mUid=0 statusCode=1006 reason=Disconnected
qtp100555887-66: onWebSocketClose: hostname=77.111.247.123 port=40691
mUid=0 statusCode=1006 reason=Disconnected
qtp100555887-71: onWebSocketClose: hostname=77.111.247.123 port=42557
mUid=18555 statusCode=1006 reason=Disconnected
qtp100555887-71: onWebSocketClose: hostname=77.111.247.123 port=57649
mUid=18555 statusCode=1006 reason=Disconnected
qtp100555887-71: onWebSocketClose: hostname=77.111.247.123 port=15239
mUid=18555 statusCode=1006 reason=Disconnected

Thank you
Alex
___
jetty-users mailing list
jetty-users@eclipse.org
To unsubscribe from this list, visit 
https://www.eclipse.org/mailman/listinfo/jetty-users


[jetty-users] WebSocketListener.onWebSocketClose called numerous times

2021-02-26 Thread Alexander Farber
Hello,

I am using Jetty 9.4.35.v20201120 for a Websockets word game with 2000
users and while it works pretty stable, every minute or so I observe the
following bursts in the log:

INFO:words:qtp100555887-757: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1001 reason=
INFO:words:qtp100555887-756: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1001 reason=
INFO:words:qtp100555887-757: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1001 reason=
INFO:words:qtp100555887-929: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1001 reason=
INFO:words:qtp100555887-757: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1001 reason=
INFO:words:qtp100555887-938: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1001 reason=
INFO:words:qtp100555887-937: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1001 reason=
INFO:words:qtp100555887-938: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1001 reason=
INFO:words:qtp100555887-929: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1001 reason=
INFO:words:qtp100555887-953: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1001 reason=
INFO:words:qtp100555887-756: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1001 reason=
INFO:words:qtp100555887-929: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1001 reason=
INFO:words:qtp100555887-756: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1001 reason=
INFO:words:qtp100555887-757: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1001 reason=
INFO:words:qtp100555887-938: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1001 reason=
INFO:words:qtp100555887-953: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1001 reason=
INFO:words:qtp100555887-938: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1001 reason=
INFO:words:qtp100555887-937: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1001 reason=
INFO:words:qtp100555887-757: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1001 reason=
INFO:words:qtp100555887-953: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1001 reason=
INFO:words:qtp100555887-929: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1001 reason=
INFO:words:qtp100555887-938: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1001 reason=
INFO:words:qtp100555887-756: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1001 reason=
INFO:words:qtp100555887-937: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1001 reason=
INFO:words:qtp100555887-938: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1001 reason=
INFO:words:qtp100555887-756: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1001 reason=
INFO:words:qtp100555887-757: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1001 reason=
INFO:words:qtp100555887-938: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1001 reason=
INFO:words:qtp100555887-937: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1001 reason=
INFO:words:qtp100555887-929: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1001 reason=
INFO:words:qtp100555887-951: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1001 reason=
INFO:words:qtp100555887-757: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1006 reason=Disconnected
INFO:words:qtp100555887-951: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1006 reason=Disconnected
INFO:words:qtp100555887-938: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1006 reason=Disconnected
INFO:words:qtp100555887-951: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1006 reason=Disconnected
INFO:words:qtp100555887-937: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1006 reason=Disconnected
INFO:words:qtp100555887-938: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1006 reason=Disconnected
INFO:words:qtp100555887-951: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1006 reason=Disconnected
INFO:words:qtp100555887-953: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1006 reason=Disconnected
INFO:words:qtp100555887-756: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1006 reason=Disconnected
INFO:words:qtp100555887-757: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1006 reason=Disconnected
INFO:words:qtp100555887-929: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1006 reason=Disconnected
INFO:words:qtp100555887-756: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1006 reason=Disconnected
INFO:words:qtp100555887-951: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1006 reason=Disconnected
INFO:words:qtp100555887-938: onWebSocketClose: hostname=77.111.244.123
mUid=0 statusCode=1006 reason=Disconnected
INFO:words:qtp100555887-951: onWebSocketClose: 

Re: Deleting takes days, should I add some index?

2021-02-25 Thread Alexander Farber
Thank you for the explanation, David

On Thu, Feb 25, 2021 at 9:49 PM David Rowley  wrote:

>
> Since your foreign keys perform a cascade delete on the tables
> referencing the tables you're deleting from, any records in those
> referencing tables will be deleted too.  You must also look at those
> referencing tables and see what references those and index the
> column(s) which are referencing.
>
>


Re: Deleting takes days, should I add some index?

2021-02-25 Thread Alexander Farber
Thank you, Pavel!

I didn't even think about trying to "explain analyze" deletion of just 1
record -

On Thu, Feb 25, 2021 at 10:04 PM Pavel Stehule 
wrote:

> čt 25. 2. 2021 v 19:39 odesílatel Alexander Farber <
>> alexander.far...@gmail.com> napsal:
>>
>>> The question is why does the command take days (when I tried last time):
>>> delete from words_games where created < now() - interval '12 month';
>>>
>>>
>> postgres=# explain analyze delete from words_games where gid = 44877;
>>
>> create index on words_scores(mid);
>>
>
I have also added:

 create index on words_puzzles(mid);

and then the result if finally good enough for my nightly cronjob:

 explain analyze delete from words_games where created < now() - interval
'12 month';
QUERY PLAN

--
 Delete on words_games  (cost=0.00..49802.33 rows=104022 width=6) (actual
time=2121.475..2121.476 rows=0 loops=1)
   ->  Seq Scan on words_games  (cost=0.00..49802.33 rows=104022 width=6)
(actual time=0.006..85.908 rows=103166 loops=1)
 Filter: (created < (now() - '1 year'::interval))
 Rows Removed by Filter: 126452
 Planning Time: 0.035 ms
 Trigger for constraint words_chat_gid_fkey on words_games: time=598.444
calls=103166
 Trigger for constraint words_moves_gid_fkey on words_games: time=83745.244
calls=103166
 Trigger for constraint words_scores_gid_fkey on words_games:
time=30638.420 calls=103166
 Trigger for constraint words_puzzles_mid_fkey on words_moves:
time=15426.679 calls=3544242
 Trigger for constraint words_scores_mid_fkey on words_moves:
time=18546.115 calls=3544242
 Execution Time: 151427.183 ms
(11 rows)

There is one detail I don't understand in the output of "explain analyze" -
why do the lines

"Trigger for constraint words_scores_mid_fkey on words_moves: time=1885.372
calls=4"

completely disappear after adding the index? Are those the "ON DELETE
CASCADE" triggers?

Aren't they called after the index has been added?

Best regards
Alex


Re: Deleting takes days, should I add some index?

2021-02-25 Thread Alexander Farber
Pavel, thank you for asking!

I have put the anonymized dump of my database at:

http://wordsbyfarber.com/words_dev.sql.gz (beware, it is a 1.3 GB download)

The question is why does the command take days (when I tried last time):

delete from words_games where created < now() - interval '12 month';


Re: Deleting takes days, should I add some index?

2021-02-25 Thread Alexander Farber
Hi Pavel,

trying to follow your advice "You should check so all foreign keys have an
index" I look at the table where I want to delete older records:

# \d words_games
  Table "public.words_games"
  Column  |   Type   | Collation | Nullable |
   Default
--+--+---+--+--
 gid  | integer  |   | not null |
nextval('words_games_gid_seq'::regclass)
 created  | timestamp with time zone |   | not null |
 finished | timestamp with time zone |   |  |
 player1  | integer  |   | not null |
 player2  | integer  |   |  |
 played1  | timestamp with time zone |   |  |
 played2  | timestamp with time zone |   |  |
 state1   | text |   |  |
 state2   | text |   |  |
 reason   | text |   |  |
 hint1| text |   |  |
 hint2| text |   |  |
 score1   | integer  |   | not null |
 score2   | integer  |   | not null |
 chat1| integer  |   | not null |
 chat2| integer  |   | not null |
 hand1| character(1)[]   |   | not null |
 hand2| character(1)[]   |   | not null |
 pile | character(1)[]   |   | not null |
 letters  | character(1)[]   |   | not null |
 values   | integer[]|   | not null |
 bid  | integer  |   | not null |
 diff1| integer  |   |  |
 diff2| integer  |   |  |
 open1| boolean  |   | not null | false
 open2| boolean  |   | not null | false
Indexes:
"words_games_pkey" PRIMARY KEY, btree (gid)
"words_games_created_idx" btree (created)
"words_games_player1_coalesce_idx" btree (player1, COALESCE(finished,
'infinity'::timestamp with time zone))
"words_games_player2_coalesce_idx" btree (player2, COALESCE(finished,
'infinity'::timestamp with time zone))
Check constraints:
"words_games_chat1_check" CHECK (chat1 >= 0)
"words_games_chat2_check" CHECK (chat2 >= 0)
"words_games_check" CHECK (player1 <> player2)
"words_games_score1_check" CHECK (score1 >= 0)
"words_games_score2_check" CHECK (score2 >= 0)
Foreign-key constraints:
"words_games_bid_fkey" FOREIGN KEY (bid) REFERENCES words_boards(bid)
ON DELETE CASCADE
"words_games_player1_fkey" FOREIGN KEY (player1) REFERENCES
words_users(uid) ON DELETE CASCADE
"words_games_player2_fkey" FOREIGN KEY (player2) REFERENCES
words_users(uid) ON DELETE CASCADE
Referenced by:
TABLE "words_chat" CONSTRAINT "words_chat_gid_fkey" FOREIGN KEY (gid)
REFERENCES words_games(gid) ON DELETE CASCADE
TABLE "words_moves" CONSTRAINT "words_moves_gid_fkey" FOREIGN KEY (gid)
REFERENCES words_games(gid) ON DELETE CASCADE
TABLE "words_scores" CONSTRAINT "words_scores_gid_fkey" FOREIGN KEY
(gid) REFERENCES words_games(gid) ON DELETE CASCADE

You are probably talking about the section:

Foreign-key constraints:
"words_games_bid_fkey" FOREIGN KEY (bid) REFERENCES words_boards(bid)
ON DELETE CASCADE
"words_games_player1_fkey" FOREIGN KEY (player1) REFERENCES
words_users(uid) ON DELETE CASCADE
"words_games_player2_fkey" FOREIGN KEY (player2) REFERENCES
words_users(uid) ON DELETE CASCADE

The first table words_boards only has 4 records, so I ignore it.

The second table words_users already has an index on the uid, because that
column is the primary key:

#  \d words_users
Table "public.words_users"
   Column|   Type   | Collation | Nullable |
 Default
-+--+---+--+--
 uid | integer  |   | not null |
nextval('words_users_uid_seq'::regclass)
 created | timestamp with time zone |   | not null |
 visited | timestamp with time zone |   | not null |
 ip  | inet |   | not null |
 fcm | text |   |  |
 apns| text |   |  |
 adm | text |   |  |
 motto   | text |   |  |
 vip_until   | timestamp with time zone |   |  |
 grand_until | timestamp with time zone |   |  |
 elo | integer  |   | not null |
 medals  | integer  |   | not null |
 

Re: Deleting takes days, should I add some index?

2021-02-25 Thread Alexander Farber
Hello, revisiting an older mail on the too long deletion times (in
PostgreSQL 13.2)...

I have followed the advices here, thank you -

On Fri, Nov 27, 2020 at 4:15 PM Guillaume Lelarge 
wrote:

> Le ven. 27 nov. 2020 à 16:05, Alvaro Herrera  a
> écrit :
>
>> On 2020-Nov-27, Alexander Farber wrote:
>>
>> > Referenced by:
>> > TABLE "words_chat" CONSTRAINT "words_chat_gid_fkey" FOREIGN KEY
>> (gid)
>> > REFERENCES words_games(gid) ON DELETE CASCADE
>> > TABLE "words_moves" CONSTRAINT "words_moves_gid_fkey" FOREIGN KEY
>> (gid)
>> > REFERENCES words_games(gid) ON DELETE CASCADE
>> > TABLE "words_scores" CONSTRAINT "words_scores_gid_fkey" FOREIGN KEY
>> > (gid) REFERENCES words_games(gid) ON DELETE CASCADE
>>
>> Make sure you have indexes on the gid columns of these tables.  Delete
>> needs to scan them in order to find the rows that are cascaded to.
>>
>>
> An index on words_games(finished) and words_moves(played) would help too.
>
>
and have now the following indices in my database:

CREATE INDEX ON words_games(player1, COALESCE(finished, 'INFINITY'));
CREATE INDEX ON words_games(player2, COALESCE(finished, 'INFINITY'));
CREATE INDEX ON words_games(created),
CREATE INDEX ON words_chat(created),
CREATE INDEX ON words_moves(uid, action, played);
CREATE INDEX ON words_moves(gid, played);
CREATE INDEX ON words_moves(played);
CREATE INDEX ON words_moves(uid);
CREATE INDEX ON words_moves(gid);
CREATE INDEX ON words_social(uid, stamp);
CREATE INDEX ON words_geoip USING SPGIST (block);
CREATE INDEX ON words_scores(LENGTH(word), mid);
-- CREATE INDEX ON words_scores(uid, LENGTH(word) desc);
CREATE INDEX ON words_scores(gid);
CREATE INDEX ON words_scores(uid);
CREATE INDEX ON words_chat(gid);

However the deletion still takes forever and I have to ctrl-c it:

# delete from words_games where created < now() - interval '12 month';

Do you please have any further suggestions?

When I try to prepend "explain analyze" to the above query, then in the
production database it also lasts forever.

In an empty dev database the output does not help much -

# explain analyze delete from words_games where created < now() - interval
'12 month';
 QUERY PLAN

 Delete on words_games  (cost=0.00..40.34 rows=1 width=6) (actual
time=0.132..0.132 rows=0 loops=1)
   ->  Seq Scan on words_games  (cost=0.00..40.34 rows=1 width=6) (actual
time=0.131..0.131 rows=0 loops=1)
 Filter: (created < (now() - '1 year'::interval))
 Rows Removed by Filter: 137
 Planning Time: 0.150 ms
 Execution Time: 0.143 ms
(6 rows)

Below are the words_games and the "referenced by" tables -

# \d words_games
  Table "public.words_games"
  Column  |   Type   | Collation | Nullable |
   Default
--+--+---+--+--
 gid  | integer  |   | not null |
nextval('words_games_gid_seq'::regclass)
 created  | timestamp with time zone |   | not null |
 finished | timestamp with time zone |   |  |
 player1  | integer  |   | not null |
 player2  | integer  |   |  |
 played1  | timestamp with time zone |   |  |
 played2  | timestamp with time zone |   |  |
 state1   | text |   |  |
 state2   | text |   |  |
 reason   | text |   |  |
 hint1| text |   |  |
 hint2| text |   |  |
 score1   | integer  |   | not null |
 score2   | integer  |   | not null |
 chat1| integer  |   | not null |
 chat2| integer  |   | not null |
 hand1| character(1)[]   |   | not null |
 hand2| character(1)[]   |   | not null |
 pile | character(1)[]   |   | not null |
 letters  | character(1)[]   |   | not null |
 values   | integer[]|   | not null |
 bid  | integer  |   | not null |
 diff1| integer  |   |  |
 diff2| integer  |   |  |
 open1| boolean  |   | not null | false
 open2| boolean  |   | not null | false
Indexes:
"words_games_pkey" PRIMARY KEY, btree (gid)
&qu

Re: JSONB_AGG: aggregate function calls cannot be nested

2021-02-22 Thread Alexander Farber
Ahh, thank you all -

 select row_to_json (x) FROM( SELECT
jsonb_agg(day) AS day,
jsonb_agg(completed) AS completed,
jsonb_agg(expired) AS expired
from (
SELECT TO_CHAR(finished, '-MM-DD') AS day,
count(*) filter (where reason in ('regular', 'resigned')) AS
completed,
count(*) filter (where reason = 'expired') AS expired
FROM words_games
WHERE finished > CURRENT_TIMESTAMP - INTERVAL '2 week'
GROUP BY day
) t
ORDER BY day) x;



row_to_json



--
--

 {"day":["2021-02-16", "2021-02-20", "2021-02-10", "2021-02-09",
"2021-02-15", "2021-02-19", "2021-02-17", "2021-02-11", "2021-02-22",
"2021-02-08", "2021-02-
14", "2021-02-21", "2021-02-12", "2021-02-13",
"2021-02-18"],"completed":[744, 802, 864, 770, 767, 745, 837, 792, 751, 32,
843, 808, 838, 853, 751],"expired":
[237, 168, 230, 263, 203, 257, 206, 184, 337, 11, 231, 380, 231, 293, 196]}
(1 row)


Re: JSONB_AGG: aggregate function calls cannot be nested

2021-02-22 Thread Alexander Farber
Thank you Thomas, this results in

 select
day AS day,
jsonb_agg(completed) AS completed,
jsonb_agg(expired) AS expired
from (
SELECT TO_CHAR(finished, '-MM-DD') AS day,
count(*) filter (where reason in ('regular', 'resigned')) AS
completed,
count(*) filter (where reason = 'expired') AS expired
FROM words_games
WHERE finished > CURRENT_TIMESTAMP - INTERVAL '2 week'
GROUP BY day
) t
GROUP BY day
ORDER BY day;
day | completed | expired
+---+-
 2021-02-08 | [481] | [155]
 2021-02-09 | [770] | [263]
 2021-02-10 | [864] | [230]
 2021-02-11 | [792] | [184]
 2021-02-12 | [838] | [231]
 2021-02-13 | [853] | [293]
 2021-02-14 | [843] | [231]
 2021-02-15 | [767] | [203]
 2021-02-16 | [744] | [237]
 2021-02-17 | [837] | [206]
 2021-02-18 | [751] | [196]
 2021-02-19 | [745] | [257]
 2021-02-20 | [802] | [168]
 2021-02-21 | [808] | [380]
 2021-02-22 | [402] | [255]
(15 rows)

but how to get a JSON map of lists here? I am trying:

select row_to_json (x) FROM (SELECT
day AS day,
jsonb_agg(completed) AS completed,
jsonb_agg(expired) AS expired
from (
SELECT TO_CHAR(finished, '-MM-DD') AS day,
count(*) filter (where reason in ('regular', 'resigned')) AS
completed,
count(*) filter (where reason = 'expired') AS expired
FROM words_games
WHERE finished > CURRENT_TIMESTAMP - INTERVAL '2 week'
GROUP BY day
) t
GROUP BY day
ORDER BY day) x;
  row_to_json

 {"day":"2021-02-08","completed":[475],"expired":[155]}
 {"day":"2021-02-09","completed":[770],"expired":[263]}
 {"day":"2021-02-10","completed":[864],"expired":[230]}
 {"day":"2021-02-11","completed":[792],"expired":[184]}
 {"day":"2021-02-12","completed":[838],"expired":[231]}
 {"day":"2021-02-13","completed":[853],"expired":[293]}
 {"day":"2021-02-14","completed":[843],"expired":[231]}
 {"day":"2021-02-15","completed":[767],"expired":[203]}
 {"day":"2021-02-16","completed":[744],"expired":[237]}
 {"day":"2021-02-17","completed":[837],"expired":[206]}
 {"day":"2021-02-18","completed":[751],"expired":[196]}
 {"day":"2021-02-19","completed":[745],"expired":[257]}
 {"day":"2021-02-20","completed":[802],"expired":[168]}
 {"day":"2021-02-21","completed":[808],"expired":[380]}
 {"day":"2021-02-22","completed":[410],"expired":[255]}
(15 rows)

While I would actually need:

{
   "day": [ "2021-02-08", "2021-02-09", ... ],
   "completed": [ 475, 770, ...],
   "expired": [ 155, 263 , ...]
}

And then I could feed the data into the Chart.js shown at the bottom of my
web page https://slova.de/top

Currently I do a simple SELECT query and construct the JSON map of list in
the Java code of my servlet

Thank you
Alex


Re: JSONB_AGG: aggregate function calls cannot be nested

2021-02-20 Thread Alexander Farber
Ah, thank you...

JSON support in PostgreSQL is cool and seems to be extended with each
release.

But standard tasks of returning a JSON map of lists or JSON list of list
seem to be difficult to use.

Greetings
Alex


Re: JSONB_AGG: aggregate function calls cannot be nested

2021-02-20 Thread Alexander Farber
Then I have to split the query in 3 similar ones (with same condition)?

I try:

SELECT
JSONB_AGG(TO_CHAR(finished, '-MM-DD')) AS day
FROM words_games
WHERE finished > CURRENT_TIMESTAMP - INTERVAL '2 week'
GROUP BY day
ORDER BY day;
ERROR:  aggregate functions are not allowed in GROUP BY
LINE 2: JSONB_AGG(TO_CHAR(finished, '-MM-DD')) A...
^


JSONB_AGG: aggregate function calls cannot be nested

2021-02-20 Thread Alexander Farber
Good evening,

I have the following query in 13.2:

# SELECT
TO_CHAR(finished, '-MM-DD') AS day,
SUM(CASE WHEN reason='regular' or reason='resigned' THEN 1
ELSE 0 END)::int AS completed,
SUM(CASE WHEN reason='expired' THEN 1 ELSE 0 END)::int AS
expired
FROM words_games
WHERE finished > CURRENT_TIMESTAMP - INTERVAL '2 week'
GROUP BY day
ORDER BY day;
day | completed | expired
+---+-
 2021-02-06 |   167 |  71
 2021-02-07 |   821 | 189
 2021-02-08 |   816 | 323
 2021-02-09 |   770 | 263
 2021-02-10 |   864 | 230
 2021-02-11 |   792 | 184
 2021-02-12 |   838 | 231
 2021-02-13 |   853 | 293
 2021-02-14 |   843 | 231
 2021-02-15 |   767 | 203
 2021-02-16 |   744 | 237
 2021-02-17 |   837 | 206
 2021-02-18 |   751 | 196
 2021-02-19 |   745 | 257
 2021-02-20 |   654 | 135
(15 rows)

It works well, but I would like to transform it into a JSONB map with 3
arrays.

So I am trying:

# SELECT
JSONB_AGG(TO_CHAR(finished, '-MM-DD')) AS day,
JSONB_AGG(SUM(CASE WHEN reason='regular' or
reason='resigned' THEN 1 ELSE 0 END)::int) AS completed,
JSONB_AGG(SUM(CASE WHEN reason='expired' THEN 1 ELSE 0
END)::int) AS expired
FROM words_games
WHERE finished > CURRENT_TIMESTAMP - INTERVAL '2 week'
GROUP BY day
ORDER BY day;
ERROR:  aggregate function calls cannot be nested
LINE 3: JSONB_AGG(SUM(CASE WHEN reason='regular' or ...
  ^

Shouldn't I use JSONB_AGG here, to build the 3 JSON arrays?

Or is the syntax error about being able to use JSONB_AGG only once per
SELECT query?

Greetings
Alex


Re: Please help to improve SELECT JOIN performance, EXPLAIN ANALYZE attached

2021-02-20 Thread Alexander Farber
With shared_buffers=16GB, pg_top shows:


last pid:  2613;  load avg:  0.49,  0.45,  0.37;   up 0+00:19:21
 16:41:16
16 processes: 16 sleeping
CPU states:  9.2% user,  0.0% nice,  0.2% system, 90.4% idle,  0.1% iowait
Memory: 21G used, 42G free, 29M buffers, 18G cached
DB activity:  39 tps,  0 rollbs/s,   0 buffer r/s, 100 hit%,   3625 row
r/s,   11 row w/s
DB I/O: 0 reads/s, 0 KB/s, 0 writes/s, 0 KB/s
DB disk: 0.0 GB total, 0.0 GB free (100% used)
Swap: 32G free

  PID USERNAME PRI NICE  SIZE   RES STATE   TIME   WCPUCPU COMMAND
 2114 postgres  200   17G 6378M sleep   2:11  0.77% 44.80% postgres:
words words_ru [local] idle
 2012 postgres  200   17G 6005M sleep   0:55  0.45% 26.30% postgres:
words words_ru [local] idle
 2107 postgres  200   17G 6175M sleep   1:16  0.02%  0.97% postgres:
words words_ru [local] idle
 1184 postgres  200   17G   22M sleep   0:00  0.02%  0.97% postgres:
walwriter
 2030 postgres  200   17G 5948M sleep   1:18  0.00%  0.00% postgres:
words words_ru [local] idle
 2013 postgres  200   17G 5724M sleep   0:57  0.00%  0.00% postgres:
words words_ru [local] idle
 2103 postgres  200   17G 5823M sleep   0:52  0.00%  0.00% postgres:
words words_ru [local] idle
 2031 postgres  200   17G 5619M sleep   0:41  0.00%  0.00% postgres:
words words_ru [local] idle
 2029 postgres  200   17G 1128M sleep   0:00  0.00%  0.00% postgres:
words words_ru [local] idle
 2104 postgres  200   17G  948M sleep   0:00  0.00%  0.00% postgres:
words words_ru [local] idle
 2106 postgres  200   17G 1257M sleep   0:00  0.00%  0.00% postgres:
words words_ru [local] idle
 1182 postgres  200   17G  231M sleep   0:00  0.00%  0.00% postgres:
checkpointer
 1183 postgres  200   17G  135M sleep   0:00  0.00%  0.00% postgres:
background writer
 1185 postgres  200   17G 8696K sleep   0:00  0.00%  0.00% postgres:
autovacuum launcher
 2614 postgres  200   17G   22M sleep   0:00  0.00%  0.00% postgres:
words words_ru [local] idle
 1187 postgres  200   17G 6764K sleep   0:00  0.00%  0.00% postgres:
logical replication launcher


On Sat, Feb 20, 2021 at 4:15 PM Alexander Farber 
wrote:

> Thank you all, I will try at first
>
> shared_buffers = 16GBand
>
> index on words_scores(uid, length(word) desc)
>
>
>


Re: Please help to improve SELECT JOIN performance, EXPLAIN ANALYZE attached

2021-02-20 Thread Alexander Farber
Thank you all, I will try at first

shared_buffers = 16GBand

index on words_scores(uid, length(word) desc)


Re: Please help to improve SELECT JOIN performance, EXPLAIN ANALYZE attached

2021-02-20 Thread Alexander Farber
Yes, Michael, that I have noticed too, but should have written more in my
original mail.

The query when I try it does run in 15ms, but evening logs show the query
(I think only small percentage of it) running 1-3s.

At the same time my CentOS 8 server with 64 GB RAM is never loaded, the
load average show by top is 0.35-0.45.

What could be the reason, does PostgreSQL 13.2 has some scheduled task
maybe?

I have only changed few lines in the postgresql.conf:

# diff postgresql.conf.OLD postgresql.conf
64c64
< max_connections = 100 # (change requires restart)
---
> max_connections = 120 # (change requires restart)
130c130
< #work_mem = 4MB   # min 64kB
---
> work_mem = 8MB# min 64kB
132c132
< #maintenance_work_mem = 64MB  # min 1MB
---
> maintenance_work_mem = 128MB  # min 1MB
393c393
< #effective_cache_size = 4GB
---
> effective_cache_size = 8GB
410,411c410,411
< #from_collapse_limit = 8
< #join_collapse_limit = 8  # 1 disables collapsing of explicit
---
> from_collapse_limit = 24
> join_collapse_limit = 24  # 1 disables collapsing of explicit
469c469
< #log_min_messages = warning   # values in order of decreasing
detail:
---
> log_min_messages = notice # values in order of decreasing
detail:
497c497
< #log_min_duration_statement = -1  # -1 is disabled, 0 logs all
statements
---
> log_min_duration_statement = 1000 # -1 is disabled, 0 logs all
statements

And I have pgbouncer in front of the PostgreSQL:

 diff pgbouncer.ini.OLD pgbouncer.ini
12a13,15
> words_de = host=/tmp user=xxx password=xxx dbname=words_de
> words_en = host=/tmp user=xxx  password=xxx dbname=words_en
> words_ru = host=/tmp user=xxx password=xxx dbname=words_ru
115a119
> ;; auth_type = md5
148c152
< ;server_reset_query = DISCARD ALL
---
> server_reset_query = DISCARD ALL
156c160
< ;ignore_startup_parameters = extra_float_digits
---
> ignore_startup_parameters = extra_float_digits
180c184
< ;max_client_conn = 100
---
> max_client_conn = 1000
185c189
< ;default_pool_size = 20
---
> default_pool_size = 100

Or is maybe everything ok...? The daily gzipped dump grows big:

939343358 Dec 31 01:33 words_ru-Dec.sql.gz
1221682336 Jan 31 01:33 words_ru-Jan.sql.gz
1423324283 Feb 20 01:34 words_ru-Feb.sql.gz

and being an inexperienced pg admin I am a bit worried.

Thank you
Alex

>


Please help to improve SELECT JOIN performance, EXPLAIN ANALYZE attached

2021-02-19 Thread Alexander Farber
Good evening,

I have a word game which uses PostgreSQL 13.2 and 80% of the code is
written as stored functions in PL/PgSQL or SQL.

Recently I have purchased some traffic and the number of daily games
increased from 100 to 700.

In the PostgreSQL log I have noticed that the duration for 2 particular
queries have increased, especially in the evenings:

2021-02-19 17:51:19.104 CET [68932] LOG:  duration: 2356.723 ms  execute
: SELECT words_stat_longest($1::int)
2021-02-19 17:55:23.290 CET [68602] LOG:  duration: 2326.507 ms  execute
: SELECT words_stat_longest($1::int)
2021-02-19 17:57:57.057 CET [68932] LOG:  duration: 1257.773 ms  execute
: SELECT out_uidAS uid,  out_fcmAS fcm,out_apns   AS
apns,   out_admAS adm,out_hmsAS hms,out_social AS social,
out_sidAS sid,out_body   AS bodyFROM words_play_game($1::int,
$2::int, $3::jsonb)
2021-02-19 18:02:51.359 CET [68603] LOG:  duration: 2305.950 ms  execute
: SELECT words_stat_longest($1::int)
2021-02-19 18:08:26.130 CET [68932] LOG:  duration: 2375.713 ms  execute
: SELECT words_stat_longest($1::int)

One of the queries is actually a simple SELECT ... LIMIT 15 to find the 15
longest words played by a user (they are displayed at the player profile
page).

I have run the EXPLAIN ANALYZE here: https://explain.depesz.com/s/IcqN

It is difficult for me to understand, what could be missing there, does
anybody please have an idea?

Thank you for any input
Alex

P.S. Here are the tables involved in the SELECT query, with the
words_moves and words_geoip holding the most records:

# \d words_scores
Table "public.words_scores"
 Column |  Type   | Collation | Nullable | Default
+-+---+--+-
 mid| bigint  |   | not null |
 gid| integer |   | not null |
 uid| integer |   | not null |
 word   | text|   | not null |
 score  | integer |   | not null |
Indexes:
"words_scores_gid_idx" btree (gid)
"words_scores_length_mid_idx" btree (length(word) DESC, mid DESC)
"words_scores_uid_idx" btree (uid)
Check constraints:
"words_scores_score_check" CHECK (score >= 0)
"words_scores_word_check" CHECK (word ~ '^[А-Я]{2,}$'::text)
Foreign-key constraints:
"words_scores_gid_fkey" FOREIGN KEY (gid) REFERENCES words_games(gid)
ON DELETE CASCADE
"words_scores_mid_fkey" FOREIGN KEY (mid) REFERENCES words_moves(mid)
ON DELETE CASCADE
"words_scores_uid_fkey" FOREIGN KEY (uid) REFERENCES words_users(uid)
ON DELETE CASCADE

# \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  |   |  |
 str | text |   |  |
 hand| text |   |  |
 letters | character(1)[]   |   |  |
 values  | integer[]|   |  |
Indexes:
"words_moves_pkey" PRIMARY KEY, btree (mid)
"words_moves_gid_played_idx" btree (gid, played DESC)
"words_moves_uid_action_played_idx" btree (uid, action, played)
"words_moves_uid_idx" btree (uid)
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_puzzles" CONSTRAINT "words_puzzles_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

# \d words_geoip
 Table "public.words_geoip"
 Column |   Type   | Collation | Nullable | Default
+--+---+--+-
 block  | inet |   | not null |
 lat| double precision |   |  |
 lng| double precision |   |  |
Indexes:
"words_geoip_pkey" PRIMARY KEY, btree (block)

# \d words_games
  Table "public.words_games"
  Column  |   Type   | Collation | Nullable |
   Default
--+--+---+--+--
 gid  | integer  |   | not 

Re: How to return a jsonb list of lists (with integers)

2021-02-17 Thread Alexander Farber
I have tried switching to SELECT INTO, but IF FOUND is still always true,
which gives me [ null, null, null ] for some users:

SELECT JSONB_BUILD_ARRAY(
SUM(CASE WHEN (player1 = in_viewer AND state1 =
'won')  OR (player2 = in_viewer AND state2 = 'won')  THEN 1 ELSE 0
END)::integer,
SUM(CASE WHEN (player1 = in_viewer AND state1 =
'lost') OR (player2 = in_viewer AND state2 = 'lost') THEN 1 ELSE 0
END)::integer,
SUM(CASE WHEN (player1 = in_viewer AND state1 =
'draw') OR (player2 = in_viewer AND state2 = 'draw') THEN 1 ELSE 0
END)::integer
)
FROM words_games
WHERE finished IS NOT NULL
AND in_viewer IN (player1, player2)
AND in_uid IN (player1, player2)
INTO _versus;

IF FOUND THEN   -- for some reason this is always
true
out_data := JSONB_INSERT(out_data, '{versus}',
_versus);
END IF;

What works for me is:

IF _versus <> '[null,null,null]'::jsonb THEN
out_data := JSONB_INSERT(out_data, '{versus}',
_versus);
END IF;

Greetings
Alex


Re: How to return a jsonb list of lists (with integers)

2021-02-17 Thread Alexander Farber
Hi Pavel,

why would SELECT INTO be better here?

Thanks
Alex


Re: How to return a jsonb list of lists (with integers)

2021-02-17 Thread Alexander Farber
I have ended up with the following (to avoid returning [null, null, null]
for players who never played with each other):

_versus := JSONB_BUILD_ARRAY(
SUM(CASE WHEN (player1 = in_uid AND state1 =
'won')  OR (player2 = in_uid AND state2 = 'won')  THEN 1 ELSE 0
END)::integer,
SUM(CASE WHEN (player1 = in_uid AND state1 =
'lost') OR (player2 = in_uid AND state2 = 'lost') THEN 1 ELSE 0
END)::integer,
SUM(CASE WHEN (player1 = in_uid AND state1 =
'draw') OR (player2 = in_uid AND state2 = 'draw') THEN 1 ELSE 0
END)::integer
)
FROM words_games
WHERE finished IS NOT NULL
AND (
(player1 = in_uid AND player2 = in_opponent) OR
(player2 = in_uid AND player1 = in_opponent)
);

IF _versus <> '[null, null, null]'::jsonb THEN
out_data := JSONB_INSERT(out_data, '{versus}',
_versus);
END IF;


Re: How to return a jsonb list of lists (with integers)

2021-02-17 Thread Alexander Farber
I have tried the following, but IF FOUND is always false for some reason:

_versus := JSONB_BUILD_ARRAY(
SUM(CASE WHEN (player1 = in_uid AND state1 =
'won')  OR (player2 = in_uid AND state2 = 'won')  THEN 1 ELSE 0
END)::integer,
SUM(CASE WHEN (player1 = in_uid AND state1 =
'lost') OR (player2 = in_uid AND state2 = 'lost') THEN 1 ELSE 0
END)::integer,
SUM(CASE WHEN (player1 = in_uid AND state1 =
'draw') OR (player2 = in_uid AND state2 = 'draw') THEN 1 ELSE 0
END)::integer
)
FROM words_games
WHERE finished IS NOT NULL
AND (
(player1 = in_uid AND player2 = in_opponent) OR
(player2 = in_uid AND player1 = in_opponent)
);

IF FOUND THEN
out_data := JSONB_INSERT(out_data, '{versus}',
_versus);
END IF;


Re: How to return a jsonb list of lists (with integers)

2021-02-17 Thread Alexander Farber
Hello, thank you for the helpful replies.

I have decided to go with PL/PgSQL for now and also switched from JSONB
list of lists to map of lists.

And the custom stored function below works mostly well, except for a
special case -

CREATE OR REPLACE FUNCTION words_stat_charts(
in_uid   integer,
in_opponent  integer, -- optional parameter, can be NULL
OUT out_data jsonb
) RETURNS jsonb AS
$func$
BEGIN
out_data := JSONB_BUILD_OBJECT();

-- add a JSON list with 7 integers
out_data := JSONB_INSERT(out_data, '{length}', JSONB_BUILD_ARRAY(
SUM(CASE WHEN LENGTH(word) = 2 THEN 1 ELSE 0 END)::integer,
SUM(CASE WHEN LENGTH(word) = 3 THEN 1 ELSE 0 END)::integer,
SUM(CASE WHEN LENGTH(word) = 4 THEN 1 ELSE 0 END)::integer,
SUM(CASE WHEN LENGTH(word) = 5 THEN 1 ELSE 0 END)::integer,
SUM(CASE WHEN LENGTH(word) = 6 THEN 1 ELSE 0 END)::integer,
SUM(CASE WHEN LENGTH(word) = 7 THEN 1 ELSE 0 END)::integer,
SUM(CASE WHEN LENGTH(word) > 7 THEN 1 ELSE 0 END)::integer
))

-- add a JSON list with 3 integers
FROM words_scores WHERE uid = in_uid;
out_data := JSONB_INSERT(out_data, '{results}', JSONB_BUILD_ARRAY(
SUM(CASE WHEN (player1 = in_uid AND state1 = 'won')  OR
(player2 = in_uid AND state2 = 'won')  THEN 1 ELSE 0 END)::integer,
SUM(CASE WHEN (player1 = in_uid AND state1 = 'lost') OR
(player2 = in_uid AND state2 = 'lost') THEN 1 ELSE 0 END)::integer,
SUM(CASE WHEN (player1 = in_uid AND state1 = 'draw') OR
(player2 = in_uid AND state2 = 'draw') THEN 1 ELSE 0 END)::integer
))
FROM words_games
WHERE finished IS NOT NULL
AND in_uid IN (player1, player2);

-- add a JSON list with 3 integers, but only if in_opponent param
is supplied
IF in_opponent > 0 AND in_opponent <> in_uid THEN
out_data := JSONB_INSERT(out_data, '{versus}',
JSONB_BUILD_ARRAY(
SUM(CASE WHEN (player1 = in_uid AND state1 =
'won')  OR (player2 = in_uid AND state2 = 'won')  THEN 1 ELSE 0
END)::integer,
SUM(CASE WHEN (player1 = in_uid AND state1 =
'lost') OR (player2 = in_uid AND state2 = 'lost') THEN 1 ELSE 0
END)::integer,
SUM(CASE WHEN (player1 = in_uid AND state1 =
'draw') OR (player2 = in_uid AND state2 = 'draw') THEN 1 ELSE 0
END)::integer
))
FROM words_games
WHERE finished IS NOT NULL
AND (
(player1 = in_uid AND player2 = in_opponent) OR
(player2 = in_uid AND player1 = in_opponent)
);
END IF;

END
$func$ LANGUAGE plpgsql;

The function works well:

# select * from words_stat_charts(5, 6);
 out_data
---
 {"length": [2726, 2825, 2341, 1363, 394, 126, 68], "versus": [6, 3, 0],
"results": [298, 151, 0]}
(1 row)

Except when 2 players never played with each other - then I get [ null,
null, null ]:

# select * from words_stat_charts(5, 1);
  out_data

 {"length": [2726, 2825, 2341, 1363, 394, 126, 68], "versus": [null, null,
null], "results": [298, 151, 0]}
(1 row)

Is there maybe a nice trick to completely omit "versus" from the returned
JSONB map of lists when its [ null, null, null ]?

Thank you
Alex


Re: How to return a jsonb list of lists (with integers)

2021-02-16 Thread Alexander Farber
Thank you, David, with json_build_array() it works for a single query -

SELECT
JSONB_BUILD_ARRAY(
SUM(CASE WHEN (player1 = in_uid AND state1 = 'won')  OR
(player2 = in_uid AND state2 = 'won')  THEN 1 ELSE 0 END)::integer,
SUM(CASE WHEN (player1 = in_uid AND state1 = 'lost') OR
(player2 = in_uid AND state2 = 'lost') THEN 1 ELSE 0 END)::integer,
SUM(CASE WHEN (player1 = in_uid AND state1 = 'draw') OR
(player2 = in_uid AND state2 = 'draw') THEN 1 ELSE 0 END)::integer
)
FROM words_games
WHERE finished IS NOT NULL
AND in_uid IN (player1, player2);

But is it possible in SQL to combine all 3 queries, so that a JSONB list of
lists is returned?

I cannot use a UNION, because the first two queries return 3 columns, but
the last query returns 7 columns.

So I have to use PL/PgSQL, correct?

Best regards
Alex


Re: How to return a jsonb list of lists (with integers)

2021-02-16 Thread Alexander Farber
On Tue, Feb 16, 2021 at 7:52 PM Michael Lewis  wrote:

> Aggregate functions work on a single column to summarize many rows into
> fewer rows. You seem to be wanting to combine multiple columns which would
> be done by concatenation or array[column1,column2] or something like that.
>

Ah right, Michael, thanks - that is what I realised just after sending the
mail.

I don't have rows here, but a single row with several columns.


How to return a jsonb list of lists (with integers)

2021-02-16 Thread Alexander Farber
Good evening,

In 13.2 I have 3 SQL queries, which work well and return integer values.

The values I feed to Google Charts (and currently I switch to Chart.js).

Currently I use the queries by calling 3 different custom stored functions
by my Java servlet.

I would like to convert the functions to 1 function, in SQL or if not
possible, then PL/pgSQL.

The new function should return a JSONB list containing 3 other lists, i.e.
something like:

[ [0,0,0], [0,0,0], [0,0,0,0,0,0,0] ]

I think I should use the aggregate function jsonb_agg().

But I can't figure out how to apply it to the 3 queries below, could you
please help me?

CREATE OR REPLACE FUNCTION words_stat_charts(
in_uid  integer,
in_opponent integer
) RETURNS jsonb AS
$func$
-- how to return [ [0,0,0], [0,0,0], [0,0,0,0,0,0,0] ] ?

SELECT
SUM(CASE WHEN (player1 = in_uid AND state1 = 'won')  OR
(player2 = in_uid AND state2 = 'won')  THEN 1 ELSE 0 END)::integer,
SUM(CASE WHEN (player1 = in_uid AND state1 = 'lost') OR
(player2 = in_uid AND state2 = 'lost') THEN 1 ELSE 0 END)::integer,
SUM(CASE WHEN (player1 = in_uid AND state1 = 'draw') OR
(player2 = in_uid AND state2 = 'draw') THEN 1 ELSE 0 END)::integer
FROM words_games
WHERE finished IS NOT NULL
AND in_uid IN (player1, player2);

SELECT
SUM(CASE WHEN (player1 = in_uid AND state1 = 'won')  OR
(player2 = in_uid AND state2 = 'won')  THEN 1 ELSE 0 END)::integer,
SUM(CASE WHEN (player1 = in_uid AND state1 = 'lost') OR
(player2 = in_uid AND state2 = 'lost') THEN 1 ELSE 0 END)::integer,
SUM(CASE WHEN (player1 = in_uid AND state1 = 'draw') OR
(player2 = in_uid AND state2 = 'draw') THEN 1 ELSE 0 END)::integer
FROM words_games
WHERE finished IS NOT NULL
AND (
(player1 = in_uid AND player2 = in_opponent) OR
(player2 = in_uid AND player1 = in_opponent)
);

SELECT
SUM(CASE WHEN LENGTH(word) = 2 THEN 1 ELSE 0 END)::integer,
SUM(CASE WHEN LENGTH(word) = 3 THEN 1 ELSE 0 END)::integer,
SUM(CASE WHEN LENGTH(word) = 4 THEN 1 ELSE 0 END)::integer,
SUM(CASE WHEN LENGTH(word) = 5 THEN 1 ELSE 0 END)::integer,
SUM(CASE WHEN LENGTH(word) = 6 THEN 1 ELSE 0 END)::integer,
SUM(CASE WHEN LENGTH(word) = 7 THEN 1 ELSE 0 END)::integer,
SUM(CASE WHEN LENGTH(word) > 7 THEN 1 ELSE 0 END)::integer
FROM words_scores WHERE uid = in_uid;

$func$ LANGUAGE sql STABLE;

When I try simply wrapping the jsonb_agg() around the 3 columns in the
first query I get the syntax error:

SELECT
JSONB_AGG(
SUM(CASE WHEN (player1 = in_uid AND state1 = 'won')  OR
(player2 = in_uid AND state2 = 'won')  THEN 1 ELSE 0 END)::integer,
SUM(CASE WHEN (player1 = in_uid AND state1 = 'lost') OR
(player2 = in_uid AND state2 = 'lost') THEN 1 ELSE 0 END)::integer,
SUM(CASE WHEN (player1 = in_uid AND state1 = 'draw') OR
(player2 = in_uid AND state2 = 'draw') THEN 1 ELSE 0 END)::integer
)
FROM words_games
WHERE finished IS NOT NULL
AND in_uid IN (player1, player2);

ERROR:  function jsonb_agg(integer, integer, integer) does not exist
LINE 8: JSONB_AGG(
^
HINT:  No function matches the given name and argument types. You might
need to add explicit type casts.

Thank you for any hints
Alex


Re: Select a column and then apply JSONB_ARRAY_ELEMENTS to it

2021-01-09 Thread Alexander Farber
On Sat, Jan 9, 2021 at 3:49 PM David G. Johnston 
wrote:

> Subqueries must be enclosed in parentheses.  The parentheses that are part
> of the function call do not count.
>
>
Ah! Thank you David, this has worked now -

CREATE OR REPLACE FUNCTION words_solve_puzzle(
in_mid   bigint,
in_uid   int,
in_guess text,
OUT out_json jsonb
) RETURNS jsonb AS
$func$
DECLARE
_tilejsonb;
_letter  char;
_value   integer;
BEGIN
in_guess := UPPER(in_guess);
FOR _tile IN SELECT * FROM JSONB_ARRAY_ELEMENTS((SELECT tiles FROM
words_moves WHERE mid = in_mid))
LOOP
_letter :=  _tile->>'letter';
_value  := (_tile->>'value')::int;
-- verify that all played tiles except wildcard are found
in the suggested answer
IF _value > 0 AND POSITION(_letter IN in_guess) = 0 THEN
out_json := json_build_object(
'label',' Keep guessing!'
);
RETURN;
END IF;
END LOOP;

-- check if the in_guess is one of the played words in that move
IF NOT EXISTS(SELECT 1 FROM words_scores WHERE mid = in_mid AND
word = in_guess) THEN
out_json := json_build_object(
'label',' Wrong!'
);
RETURN;
END IF;

-- the solution already submitted, just ack, but do not award coins
IF EXISTS (SELECT 1 FROM words_puzzles WHERE mid = in_mid AND uid =
in_uid) THEN
out_json := json_build_object(
'label',' Correct!',
'url',  '/ws/puzzle2?mid=' || in_mid ||
'=' || MD5(in_mid || 'my secret')
);
RETURN;
END IF;

-- save the puzzle solution and award coins to the user
INSERT INTO words_puzzles (mid, uid, solved)
VALUES (in_mid, in_uid, CURRENT_TIMESTAMP);

UPDATE words_users
SET coins = coins + 1
WHERE uid = in_uid;

out_json := json_build_object(
'label',' Correct, +1 coin!',
'url',  '/ws/puzzle2?mid=' || in_mid || '=' ||
MD5(in_mid || 'my secret')
);
END
$func$ LANGUAGE plpgsql;

P.S. 'my secret' is not my real secret passphrase :-)


Select a column and then apply JSONB_ARRAY_ELEMENTS to it

2021-01-09 Thread Alexander Farber
Hello,

for a word puzzle using PostgreSQL 13.1:

https://wortefarbers.de/ws/puzzle2?mid=138=c6f469786df7e8d44461381b62b2ce7d

I am trying to improve a stored function -

CREATE OR REPLACE FUNCTION words_solve_puzzle(
in_mid   bigint,
in_uid   int,
in_answertext,
OUT out_json jsonb
) RETURNS jsonb AS
$func$
DECLARE
_tilejsonb;
_letter  char;
_value   integer;
_answer  text;
BEGIN
FOR _tile IN SELECT * FROM JSONB_ARRAY_ELEMENTS(SELECT tiles FROM
words_moves WHERE mid = in_mid)
LOOP
_letter :=  _tile->>'letter';
_value  := (_tile->>'value')::int;
RAISE NOTICE 'Tile % letter % value', _tile, _letter,
_value;
END LOOP;

However this results in the error message -

ERROR:  42601: syntax error at or near "SELECT"
LINE 24: ...  FOR _tile IN SELECT * FROM JSONB_ARRAY_ELEMENTS(SELECT til...
  ^
LOCATION:  scanner_yyerror, scan.l:1180

Could you please help me, how to combine SELECT query and the LOOP
through JSONB_ARRAY_ELEMENTS here?

Thank you
Alex

P.S: Here the table:

words_de=> \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  |   |  |
 str | text |   |  |
 hand| text |   |  |
 letters | character(1)[]   |   |  |
 values  | integer[]|   |  |
Indexes:
"words_moves_pkey" PRIMARY KEY, btree (mid)
"words_moves_gid_played_idx" btree (gid, played DESC)
"words_moves_uid_action_played_idx" btree (uid, action, played)
"words_moves_uid_idx" btree (uid)
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_puzzles" CONSTRAINT "words_puzzles_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


Re: SELECT but only if not present in another table

2020-12-06 Thread Alexander Farber
Thank you, Steve -

On Sun, Dec 6, 2020 at 6:50 PM Steve Baldwin 
wrote:

> Can't you just use table aliases? So, the outer word_moves would become
> 'word_moves as wm', word_puzzles would become 'word_puzzles as wp', and the
> where clause 'WHERE wp.mid = wm.mid' ?
>

table aliases have worked for me!


SELECT but only if not present in another table

2020-12-06 Thread Alexander Farber
Good evening,

in PostgreSQL 13.1 I save player moves in the table:

# \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  |   |  |
 str | text |   |  |
 hand| text |   |  |
 letters | character(1)[]   |   |  |
 values  | integer[]|   |  |
Indexes:
"words_moves_pkey" PRIMARY KEY, btree (mid)
"words_moves_gid_played_idx" btree (gid, played DESC)
"words_moves_puzzle_idx" btree (puzzle)
"words_moves_uid_action_played_idx" btree (uid, action, played)
"words_moves_uid_idx" btree (uid)
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_puzzles" CONSTRAINT "words_puzzles_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

Some of the moves can be "interesting" in the sense that the player have
used all 7 letter tiles or achieved a high score over 90 points,

I want to display those moves as "puzzles" and have prepared a table to
store, per-user, who has solved them:

# \d words_puzzles
Table "public.words_puzzles"
 Column |   Type   | Collation | Nullable | Default
+--+---+--+-
 mid| bigint   |   | not null |
 uid| integer  |   | not null |
 solved | timestamp with time zone |   | not null |
Foreign-key constraints:
"words_puzzles_mid_fkey" FOREIGN KEY (mid) REFERENCES words_moves(mid)
ON DELETE CASCADE
"words_puzzles_uid_fkey" FOREIGN KEY (uid) REFERENCES words_users(uid)
ON DELETE CASCADE

Now I am trying to create a custom stored function which would return just
one mid (move id) which is not too new (1 year old) and the user has not
tackled it yet:

CREATE OR REPLACE FUNCTION words_daily_puzzle(
in_uid   int
) RETURNS table (
out_mid  bigint,
out_secret   text
) AS
$func$
SELECT
mid,
MD5(mid ||'my little secret')
FROM words_moves
WHERE action = 'play'
AND (LENGTH(str) = 7 OR score > 90)
AND played BETWEEN CURRENT_TIMESTAMP - INTERVAL '51 week' AND
CURRENT_TIMESTAMP - INTERVAL '50 week'
-- the user has not solved this puzzle yet
AND NOT EXISTS (SELECT 1 FROM words_puzzles WHERE mid =
the_outer_mid AND uid = in_uid)
ORDER BY PLAYED ASC
LIMIT 1;
$func$ LANGUAGE sql;

As you can see I am missing 1 piece - how do I address the outer SELECT mid
from the EXISTS-SELECT?

I have written "the_outer_mid" there.

Should I use LEFT JOIN LATERAL here? I have difficulties wrapping my head
around this.

TLDR: how to return 1 mid from 1 year ago, which is not solved by the user
in_uid yet?

Thank you
Alex


Re: Using a boolean column with IF / THEN

2020-12-06 Thread Alexander Farber
On Sat, Dec 5, 2020 at 9:00 PM David G. Johnston 
wrote:

> Maybe not “simpler” but for all those checks you could write a single
> query that pulls out all the data at once into a record variable and test
> against the columns pf that instead of executing multiple queries.
>

Thank you!


Using a boolean column with IF / THEN

2020-12-05 Thread Alexander Farber
Good evening,

hopefully my question is not too stupid, but -

in a 13.1 database I have a words_users table with a boolean column:

-- the user is not allowed to chat or change the motto
muted boolean NOT NULL DEFAULT false,

Currently I check the value as follows, but I wonder if this is the best
way with PL/pgSQL -

IF EXISTS (SELECT 1 FROM words_users
WHERE uid = _uid AND
muted) THEN
RAISE EXCEPTION 'User % is muted', _uid;
END IF;

Or can this be done in a simpler way?

Thanks
Alex

P.S. Here my entire stored function:

CREATE OR REPLACE FUNCTION words_set_motto(
in_social integer,
in_sidtext,
in_auth   text,
in_motto  text
) RETURNS integer AS
$func$
DECLARE
_uid integer;
BEGIN
IF NOT words_valid_user(in_social, in_sid, in_auth) THEN
RAISE EXCEPTION 'Invalid user = % %', in_social, in_sid;
END IF;

_uid := (SELECT uid FROM words_social WHERE social = in_social AND
sid = in_sid);

IF LENGTH(in_motto) > 250 THEN
RAISE EXCEPTION 'Invalid motto by user %', _uid;
END IF;

IF EXISTS (SELECT 1 FROM words_users
WHERE uid = _uid AND
muted) THEN
RAISE EXCEPTION 'User % is muted', _uid;
END IF;

IF (SELECT
COUNT(NULLIF(nice, 0)) -
COUNT(NULLIF(nice, 1))
FROM words_reviews
WHERE uid = _uid) < -20 THEN
RAISE EXCEPTION 'User % can not change motto', _uid;
END IF;

UPDATE words_users
SET motto = in_motto
WHERE uid = _uid;

RETURN _uid;
END
$func$ LANGUAGE plpgsql;


[CentOS] CentOS 8.2: error running non-shared postrotate script for /var/log/mysql/mysqld.log

2020-12-05 Thread Alexander Farber
Hello fellow CentOS users!

I have installed CentOS 8.2.2004 with the following packages:

mysql-common-8.0.21-1.module_el8.2.0+493+63b41e36.x86_64
mysql-8.0.21-1.module_el8.2.0+493+63b41e36.x86_64
mysql-errmsg-8.0.21-1.module_el8.2.0+493+63b41e36.x86_64
mysql-server-8.0.21-1.module_el8.2.0+493+63b41e36.x86_64

Then I have run mysql_secure_installation and among other things set the
root password for MySQL

As result I am greeted with the following anachron mail every morning:

/etc/cron.daily/logrotate:

 mysqladmin: connect to server at 'localhost' failed
error: 'Access denied for user 'root'@'localhost' (using password: NO)'
error: error running non-shared postrotate script for
/var/log/mysql/mysqld.log of '/var/log/mysql/mysqld.log '

I understand that the reason is me having set the root password for MySQL.

But my question is how to provide the password to postrotate without
disclosing it too much?

Other than that the MySQL works well, I use it to host 3 Wordpress websites
at my CentOS 8 Linux server (haproxy -> Jetty x 3 -> FastCGI -> php-fpm ->
Wordpress -> MySQL)

Greetings from Germany
Alex
___
CentOS mailing list
CentOS@centos.org
https://lists.centos.org/mailman/listinfo/centos


Deleting takes days, should I add some index?

2020-11-27 Thread Alexander Farber
Hello,

I am using PostgreSQL 10.15 on CentOS 7 with 64 GB RAM, Intel i7 6700 and I
have the following 2 tables there:

words_ru=> \d words_games
  Table "public.words_games"
  Column  |   Type   | Collation | Nullable |
   Default
--+--+---+--+--
 gid  | integer  |   | not null |
nextval('words_games_gid_seq'::regclass)
 created  | timestamp with time zone |   | not null |
 finished | timestamp with time zone |   |  |
 player1  | integer  |   | not null |
 player2  | integer  |   |  |
 played1  | timestamp with time zone |   |  |
 played2  | timestamp with time zone |   |  |
 state1   | text |   |  |
 state2   | text |   |  |
 reason   | text |   |  |
 hint1| text |   |  |
 hint2| text |   |  |
 score1   | integer  |   | not null |
 score2   | integer  |   | not null |
 chat1| integer  |   | not null |
 chat2| integer  |   | not null |
 hand1| character(1)[]   |   | not null |
 hand2| character(1)[]   |   | not null |
 pile | character(1)[]   |   | not null |
 letters  | character(1)[]   |   | not null |
 values   | integer[]|   | not null |
 bid  | integer  |   | not null |
 diff1| integer  |   |  |
 diff2| integer  |   |  |
Indexes:
"words_games_pkey" PRIMARY KEY, btree (gid)
"words_games_player1_coalesce_idx" btree (player1, COALESCE(finished,
'infinity'::timestamp with time zone))
"words_games_player2_coalesce_idx" btree (player2, COALESCE(finished,
'infinity'::timestamp with time zone))
Check constraints:
"words_games_chat1_check" CHECK (chat1 >= 0)
"words_games_chat2_check" CHECK (chat2 >= 0)
"words_games_check" CHECK (player1 <> player2)
"words_games_score1_check" CHECK (score1 >= 0)
"words_games_score2_check" CHECK (score2 >= 0)
Foreign-key constraints:
"words_games_bid_fkey" FOREIGN KEY (bid) REFERENCES words_boards(bid)
ON DELETE CASCADE
"words_games_player1_fkey" FOREIGN KEY (player1) REFERENCES
words_users(uid) ON DELETE CASCADE
"words_games_player2_fkey" FOREIGN KEY (player2) REFERENCES
words_users(uid) ON DELETE CASCADE
Referenced by:
TABLE "words_chat" CONSTRAINT "words_chat_gid_fkey" FOREIGN KEY (gid)
REFERENCES words_games(gid) ON DELETE CASCADE
TABLE "words_moves" CONSTRAINT "words_moves_gid_fkey" FOREIGN KEY (gid)
REFERENCES words_games(gid) ON DELETE CASCADE
TABLE "words_scores" CONSTRAINT "words_scores_gid_fkey" FOREIGN KEY
(gid) REFERENCES words_games(gid) ON DELETE CASCADE

words_ru=> \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  |   |  |
 str | text |   |  |
 hand| text |   |  |
 puzzle  | boolean  |   | not null | false
 letters | character(1)[]   |   |  |
 values  | integer[]|   |  |
Indexes:
"words_moves_pkey" PRIMARY KEY, btree (mid)
"words_moves_gid_played_idx" btree (gid, played DESC)
"words_moves_puzzle_idx" btree (puzzle)
"words_moves_uid_action_played_idx" btree (uid, action, played)
"words_moves_uid_idx" btree (uid)
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_scores" CONSTRAINT "words_scores_mid_fkey" FOREIGN KEY
(mid) REFERENCES words_moves(mid) ON DELETE CASCADE

My word game is published since beginning of 2018 and I have that many
entries there:

words_ru=> select 

[jetty-users] Is it necessary to close session in WebSocketConnectionListener.onWebSocketError ?

2020-10-08 Thread Alexander Farber
Good evening, the doc

https://www.eclipse.org/jetty/javadoc/current/org/eclipse/jetty/websocket/api/WebSocketConnectionListener.html

says, that when onWebSocketClose is called, then the session is closed
automatically.

But what about the other callback - the onWebSocketError?

As a developer, do I have to call mSession.close() there myself? Or is it
closed already?

And another question please - is onWebSocketClose called, when I
call mSession.close()?

Best regards
Alex
___
jetty-users mailing list
jetty-users@eclipse.org
To unsubscribe from this list, visit 
https://www.eclipse.org/mailman/listinfo/jetty-users


Re: [CentOS] certbot stopped working on CentOS 7: pyOpenSSL module missing required functionality

2020-10-05 Thread Alexander Farber
Yes, I had a typo in the mail, but not in the cronjob

Still wondering how to get certbot-1.7.0-1.el7.noarch working on CentOS 7
again.
___
CentOS mailing list
CentOS@centos.org
https://lists.centos.org/mailman/listinfo/centos


[CentOS] certbot stopped working on CentOS 7: pyOpenSSL module missing required functionality

2020-10-05 Thread Alexander Farber
Hello fellow CentOS users,

I had this cronjob working for many moons on CentOS 7.8.2003:

#minute hourmdaymonth   wdaycommand
6   6   *   *   1   certbot renew --post-hook
"cat /etc/letsencrypt/live/raspasy.de/fullchain.pem /etc/letsencrypt/live/
raspasy.de/privkey.pem > /etc/letsencrypt/live/raspasy.de/haproxy.pem;
systemctl resstart haproxy"

(I run a post hook, because haproxy-1.5.18-9.el7.x86_64 from the CentOS
packages wants to have the cert and the key in one file).

Unfortunately, now certbot-1.7.0-1.el7.noarch has stopped working and the
error message is:

/usr/lib/python2.7/site-packages/josepy/util.py:9:
CryptographyDeprecationWarning: Python 2 is no longer supported by the
Python core team. Support for it is now deprecated in cryptography, and
will be removed in a future release.
  from cryptography.hazmat.primitives.asymmetric import rsa
Traceback (most recent call last):
  File "/usr/bin/certbot", line 9, in 
load_entry_point('certbot==1.7.0', 'console_scripts', 'certbot')()
  File "/usr/lib/python2.7/site-packages/pkg_resources.py", line 378, in
load_entry_point
return get_distribution(dist).load_entry_point(group, name)
  File "/usr/lib/python2.7/site-packages/pkg_resources.py", line 2566, in
load_entry_point
return ep.load()
  File "/usr/lib/python2.7/site-packages/pkg_resources.py", line 2260, in
load
entry = __import__(self.module_name, globals(),globals(), ['__name__'])
  File "/usr/lib/python2.7/site-packages/certbot/main.py", line 2, in

from certbot._internal import main as internal_main
  File "/usr/lib/python2.7/site-packages/certbot/_internal/main.py", line
20, in 
from certbot._internal import account
  File "/usr/lib/python2.7/site-packages/certbot/_internal/account.py",
line 18, in 
from acme.client import ClientBase  # pylint: disable=unused-import
  File "/usr/lib/python2.7/site-packages/acme/client.py", line 39, in

requests.packages.urllib3.contrib.pyopenssl.inject_into_urllib3()  #
type: ignore
  File "/usr/lib/python2.7/site-packages/urllib3/contrib/pyopenssl.py",
line 118, in inject_into_urllib3
_validate_dependencies_met()
  File "/usr/lib/python2.7/site-packages/urllib3/contrib/pyopenssl.py",
line 160, in _validate_dependencies_met
"'pyOpenSSL' module missing required functionality. "
ImportError: 'pyOpenSSL' module missing required functionality. Try
upgrading to v0.14 or newer.

I have the following python packages installed:

#  rpm -qa | grep python2
python2-oauthlib-2.0.1-8.el7.noarch
python2-futures-3.1.1-5.el7.noarch
python2-cryptography-1.7.2-2.el7.x86_64
python2-pip-8.1.2-14.el7.noarch
python2-acme-1.7.0-1.el7.noarch
python2-psycopg2-2.8.6-1.rhel7.x86_64
python2-certbot-1.7.0-1.el7.noarch
python2-pyrfc3339-1.1-3.el7.noarch
python2-distro-1.2.0-3.el7.noarch
python2-configargparse-0.11.0-2.el7.noarch
python2-josepy-1.3.0-2.el7.noarch
python2-pyasn1-0.1.9-7.el7.noarch
python2-six-1.9.0-0.el7.noarch
python2-parsedatetime-2.4-6.el7.noarch
python2-future-0.18.2-2.el7.noarch
python2-requests-oauthlib-0.8.0-5.el7.noarch
python2-mock-1.0.1-10.el7.noarch

Does anybody please have an idea, what could I do?

I like to use the stock packages only (for easier maintenance), wonder if a
solution is still possible there...

Thank you for any suggestions
Alex
___
CentOS mailing list
CentOS@centos.org
https://lists.centos.org/mailman/listinfo/centos


Re: How to use avro-python3 on Windows 10 to parse files?

2020-09-29 Thread Alexander Farber
Hello and thanks for your replies!

I have to apologize - the avro file I was using did not contain any useful
data. The reason for my confusion is that a colleague (in a screen sharing
session) was using a different file with the same name while testing for me.

Now I have tried both avro and fastavro modules on a different avro file
and both worked. I will look at PySpark as well.


Re: How to use avro-python3 on Windows 10 to parse files?

2020-09-28 Thread Alexander Farber
Hi Michael -

On Mon, Sep 28, 2020 at 8:19 PM Michael A. Smith 
wrote:

> Where did you find that avro-python3 is recommended? I would like to
> update that.
>

here: https://stackoverflow.com/a/43606979/165071


> avro-python3 is deprecated. Your should use the avro library instead.
>
> If the avro library for python doesn't work, please let me know.
>

Yes, it does not work for me with python 3.8.0 - please see below

On Mon, Sep 28, 2020 at 12:43 Alexander Farber 
> wrote:
>
>> With python 3.8.5 and avro 1.10.0 installed via pip I have tried running
>> the following script:
>>
>> import os, avro
>> from avro.datafile import DataFileReader, DataFileWriter
>> from avro.io import DatumReader, DatumWriter
>>
>> reader = DataFileReader(open("48.avro", "rb"), DatumReader())
>> for d in reader:
>> print(d)
>> reader.close()
>>
>> Unfortunately, nothing is printed by the script.
>>
>> https://stackoverflow.com/questions/64105500/how-to-use-avro-python3-on-windows-10-to-parse-files
>>
>
Michael, how could I debug this issue? I am an avro and python newbie

Greetings from Germany
Alex


How to use avro-python3 on Windows 10 to parse files?

2020-09-28 Thread Alexander Farber
Hello and good evening!

With python 3.8.5 and avro 1.10.0 installed via pip I have tried running
the following script:

import os, avro
from avro.datafile import DataFileReader, DataFileWriter
from avro.io import DatumReader, DatumWriter

reader = DataFileReader(open("48.avro", "rb"), DatumReader())
for d in reader:
print(d)
reader.close()

Unfortunately, nothing is printed by the script.

Then I have noticed, that the avro-python3 is recommended and have
installed it with pip install avro-python3

Now the pip list shows both packages, but how to switch to using the newer
one?

I have tried "pip uninstall avro", but then import avro fails.

What is the correct module name for avro-python3, how to import it in my
script please?

I have also asked my question at Stackoverflow and there you can see my
screenshots:
https://stackoverflow.com/questions/64105500/how-to-use-avro-python3-on-windows-10-to-parse-files

Thank you for any hints
Alex


Re: How to call JSONB_INSERT with integer as the new to-be-inserted value?

2020-09-13 Thread Alexander Farber
Thank you!


How to call JSONB_INSERT with integer as the new to-be-inserted value?

2020-09-12 Thread Alexander Farber
Good evening,

I am trying to take a JSONB object (comes from an HTTP cookie set by my
app) and add a property "uid" to it, which should hold an integer:

CREATE OR REPLACE FUNCTION words_get_user(
in_users jsonb,
OUT out_user jsonb
) RETURNS jsonb AS
$func$
DECLARE
_user   jsonb;
_uidinteger;
_banned boolean;
_removedboolean;
BEGIN
-- in_users must be a JSON array with at least 1 element
IF in_users IS NULL OR JSONB_ARRAY_LENGTH(in_users) = 0 THEN
RAISE EXCEPTION 'Invalid users = %', in_users;
END IF;

-- ensure that every record has a valid auth
FOR _user IN SELECT * FROM JSONB_ARRAY_ELEMENTS(in_users)
LOOP
IF NOT words_valid_user((_user->>'social')::int,
 _user->>'sid',
 _user->>'auth') THEN
RAISE EXCEPTION 'Invalid user = %', _user;
END IF;

IF out_user IS NULL THEN
SELECT
uid,
u.banned_until > CURRENT_TIMESTAMP,
u.removed
INTO STRICT
_uid,
_banned,
_removed
FROM words_social s
LEFT JOIN words_users u USING(uid)
WHERE s.social = (_user->>'social')::int
AND s.sid = _user->>'sid';

IF _banned THEN
RAISE EXCEPTION 'Banned user = %', _user;
END IF;

IF _removed THEN
RAISE EXCEPTION 'Removed user = %', _user;
END IF;

out_user := JSONB_INSERT(_user, '{uid}', _uid);
END IF;
END LOOP;
END
$func$ LANGUAGE plpgsql;

Unfortunately, when I run my stored function it fails:

words_en=> select out_user from
words_get_user('[{"given":"Abcde1","social":1,"auth":"xxx","stamp":1480237061,"sid":"a","photo":"
https://vk.com/images/camera_200.png
"},{"given":"Abcde2","social":2,"auth":"xxx","stamp":1477053188,"sid":"a"},{"given":"Abcde3","social":3,"auth":"xxx","stamp":1477053330,"sid":"a"}]'::jsonb);
ERROR:  function jsonb_insert(jsonb, unknown, integer) does not exist
LINE 1: SELECT JSONB_INSERT(_user, '{uid}', _uid)
   ^
HINT:  No function matches the given name and argument types. You might
need to add explicit type casts.
QUERY:  SELECT JSONB_INSERT(_user, '{uid}', _uid)
CONTEXT:  PL/pgSQL function words_get_user(jsonb) line 44 at assignment

What is missing here please?

Thank you
Alex


[jetty-users] Primary script unknown with 9.4.30.v20200611

2020-07-13 Thread Alexander Farber
Good evening,

I run the following on CentOS 7:

/usr/bin/java -Djdbc.drivers=org.postgresql.Driver -jar
/usr/share/java/jetty-distribution-9.4.30.v20200611/start.jar
jetty.home=/usr/share/java/jetty-distribution-9.4.30.v20200611
jetty.base=/var/www/jetty-base-raspasy jetty.http.host=127.0.0.1
jetty.http.port=8083

and often see the warnings in the logs:

Jul 13 20:07:19 www java[28578]: 2020-07-13
20:07:19.314:INFO:oejfch.HttpConnectionOverFCGI:qtp870698190-12: Primary
script unknown|
Jul 13 20:07:19 www java[28578]: 2020-07-13
20:07:19.584:INFO:oejfch.HttpConnectionOverFCGI:qtp870698190-18: Primary
script unknown|
Jul 13 20:07:35 www java[28578]: 2020-07-13
20:07:35.494:INFO:oejfch.HttpConnectionOverFCGI:qtp870698190-20: Primary
script unknown|
Jul 13 20:07:35 www java[28578]: 2020-07-13
20:07:35.622:INFO:oejfch.HttpConnectionOverFCGI:qtp870698190-12: Primary
script unknown|
Jul 13 20:07:40 www java[28578]: 2020-07-13
20:07:40.965:INFO:oejfch.HttpConnectionOverFCGI:qtp870698190-13: Primary
script unknown|
Jul 13 20:07:41 www java[28578]: 2020-07-13
20:07:41.140:INFO:oejfch.HttpConnectionOverFCGI:qtp870698190-11: Primary
script unknown|

What could be the reason please?

The https://serverfault.com/a/517327/61603 suggests that SCRIPT_FILENAME
would be wrong - but I do not set it anywhere or even use FastCGI (it is
just enabled, but I do not use it in the Jetty instance)

Best regards
Alex
___
jetty-users mailing list
jetty-users@eclipse.org
To unsubscribe from this list, visit 
https://www.eclipse.org/mailman/listinfo/jetty-users


[jetty-users] Setting Content-Type: text/html; charset=utf-8 for all html files

2020-04-11 Thread Alexander Farber
Good evening,

on my CentOS 7 server I run Jetty by:

# /usr/bin/java -Djdbc.drivers=org.postgresql.Driver -jar
/usr/share/java/jetty-distribution-9.4.27.v20200227/start.jar
jetty.home=/usr/share/java/jetty-distribution-9.4.27.v20200227
jetty.base=/var/www/jetty-base-ru jetty.http.host=127.0.0.1
jetty.http.port=8080

Also I serve PHP files through FastCGI as suggested at
https://www.eclipse.org/jetty/documentation/9.4.28.v20200408/configuring-fastcgi.html

My XML config file is listed at the very bottom of this mail.

However I have a problem: when I create a static HTML file in UTF-8
encoding, with no BOM, Russian language - it is misinterpreted by browsers
and they do not display Russian letters. When I rename the file. from .html
to .php then it works - because the following HTTP header is suddenly added:

Content-Type: text/html;charset=utf-8

Is there please a way to configure Jetty through the XML file below to send
the header for all HTML files?

I have searched Stackoverflow and the question seems to be complicated
because of the servlet spec.

I would prefer finding an XML option, because it would suit my deployment
and Jetty-upgrades procedure best (i.e. no editing the files distributed
with new Jetty versions, but better just configuring it once in
$JETTY_BASE/webapps)

Thank you and happy Easter :-)
Alex

PS: below is my  /var/www/jetty-base-ru/webapps/slova.de.xml, how to add
the header there?

?xml version="1.0" encoding="UTF-8"?>
http://www.eclipse.org/jetty/configure_9_3.dtd;>



/var/www/html/slova.de


/




slova.de
www.slova.de





index.php
index.html




org.eclipse.jetty.fcgi.server.proxy.TryFilesFilter
/*






files
$path /index.php?p=$path






default


org.eclipse.jetty.servlet.DefaultServlet



dirAllowed
false


gzip
true



/



org.eclipse.jetty.fcgi.server.proxy.FastCGIProxyServlet
*.php

proxyTo
http://localhost:9000


prefix
/


scriptRoot



scriptPattern
(.+?\\.php)




___
jetty-users mailing list
jetty-users@eclipse.org
To unsubscribe from this list, visit 
https://www.eclipse.org/mailman/listinfo/jetty-users


Re: [jetty-users] Configuring Jetty for FastCGI - and pass env. vars to PHP scripts

2020-04-02 Thread Alexander Farber
Yes, Simone, I will try to test it, but that will take me few days :-)

On Wed, Apr 1, 2020 at 3:03 PM Simone Bordet  wrote:

>
> Implementing #4375 was trivial and I tested it.
> Would you be able to test it on your configuration by building Jetty
> yourself?
>
>
___
jetty-users mailing list
jetty-users@eclipse.org
To unsubscribe from this list, visit 
https://www.eclipse.org/mailman/listinfo/jetty-users


Re: [jetty-users] Configuring Jetty for FastCGI - and pass env. vars to PHP scripts

2020-04-01 Thread Alexander Farber
Hello Simone -


On Tue, Mar 31, 2020 at 11:51 PM Simone Bordet  wrote:

> What you can do right now is to override this:
>
> class FarberFCGIServlet extends FastCGIProxyServlet {
>   protected void customizeFastCGIHeaders(Request proxyRequest,
> HttpFields fastCGIHeaders) {
> super.customizeFastCGIHeaders(proxyRequest, fastCGIHeaders);
> fastCGIHeaders.put("COUNTRY", System.getenv("COUNTRY"));
> ...
>   }
> }


Thank you - I have created
https://github.com/eclipse/jetty.project/issues/4735

Your suggestion for creating a custom FarberFCGIServlet  - is it to set
$_SERVER['COUNTRY'] inside the PHP scripts? Because currently that variable
is not there. Or how would I be able to retrieve the fastCGIHeaders from
PHP?

And also - how to add the custom FarberFCGIServlet  to the Jetty instances,
which are currently being run by the command below?

ExecStart=/usr/bin/java -Djdbc.drivers=org.postgresql.Driver -jar
/usr/share/java/jetty-distribution-9.4.27.v20200227/start.jar
jetty.home=/usr/share/java/jetty-distribution-9.4.27.v20200227
jetty.base=/var/www/jetty-base-en jetty.http.host=127.0.0.1
jetty.http.port=8082

Best regards
Alex
___
jetty-users mailing list
jetty-users@eclipse.org
To unsubscribe from this list, visit 
https://www.eclipse.org/mailman/listinfo/jetty-users


Re: [jetty-users] How to include another web page in a page served by Jetty?

2020-04-01 Thread Alexander Farber
On Wed, Apr 1, 2020 at 8:32 AM Greg Wilkins  wrote:

> If client side is an option, there are always simple iframes as an option
>
>
Right! Thank you for the advice
___
jetty-users mailing list
jetty-users@eclipse.org
To unsubscribe from this list, visit 
https://www.eclipse.org/mailman/listinfo/jetty-users


[jetty-users] Configuring Jetty for FastCGI - and pass env. vars to PHP scripts

2020-03-31 Thread Alexander Farber
Hello and good evening,

on a CentOS Linux server I run 3 instances of Jetty on 3 different IP
addresses, serving a servlet and  Wordpress in 3 different languages:
English, German, Russian.

The Wordpress installations are configured exactly as described at
https://www.eclipse.org/jetty/documentation/current/configuring-fastcgi.html

And the Jetty instances are started by /etc/systemd/system/jetty-en.service
(and jetty-de.service at port 8081, jetty-ru.service at 8080):

[Unit]
Description=Jetty
After=network-online.target

[Service]
Environment=COUNTRY=en
Environment=DOMAIN=wordsbyfarber.com
Environment=DATABASE_URL=jdbc:postgresql://
127.0.0.1:6432/my_database_en?user=my_user_en=my_user_password
Environment=FCM_SECRET=
Environment=AMAZON_ID=
Environment=AMAZON_SECRET=
Environment=FACEBOOK_ID=
Environment=FACEBOOK_SECRET=
Type=simple
User=jetty
Group=jetty
ExecStart=/usr/bin/java -Djdbc.drivers=org.postgresql.Driver -jar
/usr/share/java/jetty-distribution-9.4.27.v20200227/start.jar
jetty.home=/usr/share/java/jetty-distribution-9.4.27.v20200227
jetty.base=/var/www/jetty-base-en jetty.http.host=127.0.0.1
jetty.http.port=8082
SuccessExitStatus=143
Restart=always
RestartSec=180
PrivateTmp=true

[Install]
WantedBy=multi-user.target

As you can see above I pass some information through the env. variables to
the custom Java servlet of my word game.

The above stuff works well, but now I am also trying to pass some
configuration information to the few PHP scripts I am also running - and
unfortunately this does not work - when I print($_ENV['COUNTRY'); in the
PHP scripts, the env. variable is not there.

Is there please a way to pass the env. vars from Jetty process to the PHP
scripts run through FastCGI?

The reason why I am asking is that I am trying to reuse as much source code
(Java, PHP, Javascript) while serving my word game in 3 different languages.

Thank you
Alex

PS: Below is my Jetty config xml file:

# cat /var/www/jetty-base-en/webapps/wordsbyfarber.com.xml


http://www.eclipse.org/jetty/configure_9_3.dtd;>



/var/www/html/wordsbyfarber.com


/





wordsbyfarber.com
www.wordsbyfarber.com





index.php
index.html




org.eclipse.jetty.fcgi.server.proxy.TryFilesFilter
/*






files
$path /index.php?p=$path






default


org.eclipse.jetty.servlet.DefaultServlet



dirAllowed
false


gzip
true



/



org.eclipse.jetty.fcgi.server.proxy.FastCGIProxyServlet
*.php

proxyTo
http://localhost:9000


prefix
/


scriptRoot



scriptPattern
(.+?\\.php)



___
jetty-users mailing list
jetty-users@eclipse.org
To unsubscribe from this list, visit 
https://www.eclipse.org/mailman/listinfo/jetty-users


Re: [jetty-users] How to include another web page in a page served by Jetty?

2020-03-31 Thread Alexander Farber
Thank you for your response Greg -


On Mon, Mar 30, 2020 at 10:09 AM Greg Wilkins  wrote:

>
> Jetty supports the Servlet API feature of RequestDispatcher.include, so
> one URI/resource can be included in another page.  The trick is how do you
> trigger than in the content that you are generating?From the servlet,
> it should be easy enough to get a RequestDispatcher and call it directly.
> If you want to do this from static content, then you will need to use
> something like JSP to process that content and allow dynamic components to
> be added.   For wordpress... either do it in wordpress... or do something
> very complex about intercepting responses and rewriting with the new
> content - possible but a lot of work.
>

then I will better use jQuery in my test server pages to fetch and insert
the web page content

Regards
Alex
___
jetty-users mailing list
jetty-users@eclipse.org
To unsubscribe from this list, visit 
https://www.eclipse.org/mailman/listinfo/jetty-users


  1   2   3   4   5   6   7   8   9   10   >