@SoftDelete vs @SQLDelete+@SQLRestriction: bulk deletes, double deletes, converters

######## H2 ########
database: H2Dialect / H2 2.4.240 (2025-09-22)

== 1. HQL bulk delete: delete from ArticleDeleted / delete from ArticleManual
-- delete from ArticleDeleted
   sql> update article_deleted ad1_0 set deleted=true where ad1_0.deleted=false
article_deleted rows: [1 | a | true, 2 | b | true]
-- delete from ArticleManual
   sql> delete from article_manual am1_0 where (am1_0.deleted = false)
article_manual rows : []

== 2. Two sessions delete the same row (second one is stale)
-- @SQLDelete entity, s1.flush()
   sql> update article_manual set deleted = true where id = ?
flush succeeded silently (the hand-written UPDATE has no 'and deleted = false' guard)
(the @SoftDelete entity fails the same scenario with StaleStateException: see 02-ddl-and-sql.txt)

== 3. converter attribute: char(1) 'Y'/'N' column named is_gone
create table article_yes_no (is_gone char(1) not null check ((is_gone in ('N','Y'))), id bigint generated by default as identity, title varchar(255), primary key (id));
comment on column article_yes_no.is_gone is 'Y = deleted';
alive   : [1 | N]
-- remove
   sql> select ayn1_0.id,ayn1_0.title from article_yes_no ayn1_0 where ayn1_0.is_gone='N' and ayn1_0.id=?
   sql> update article_yes_no set is_gone='Y' where id=? and is_gone='N'
deleted : [1 | Y]

== 4. @SoftDelete on a @MappedSuperclass
create table note (deleted_at timestamp(6) with time zone, id bigint generated by default as identity, text varchar(255), primary key (id));
-- remove(note)
   sql> select n1_0.id,n1_0.text from note n1_0 where n1_0.deleted_at is null and n1_0.id=?
   sql> update note set deleted_at=localtimestamp where id=? and deleted_at is null
raw: [1 | true]

######## POSTGRES ########
database: PostgreSQLDialect / PostgreSQL 17.11

== 1. HQL bulk delete: delete from ArticleDeleted / delete from ArticleManual
-- delete from ArticleDeleted
   sql> update article_deleted ad1_0 set deleted=true where ad1_0.deleted=false
article_deleted rows: [1 | a | true, 2 | b | true]
-- delete from ArticleManual
   sql> delete from article_manual am1_0 where (am1_0.deleted = false)
article_manual rows : []

== 2. Two sessions delete the same row (second one is stale)
-- @SQLDelete entity, s1.flush()
   sql> update article_manual set deleted = true where id = ?
flush succeeded silently (the hand-written UPDATE has no 'and deleted = false' guard)
(the @SoftDelete entity fails the same scenario with StaleStateException: see 02-ddl-and-sql.txt)

== 3. converter attribute: char(1) 'Y'/'N' column named is_gone
create table article_yes_no (is_gone char(1) not null check ((is_gone in ('N','Y'))), id bigint generated by default as identity, title varchar(255), primary key (id));
comment on column article_yes_no.is_gone is 'Y = deleted';
alive   : [1 | N]
-- remove
   sql> select ayn1_0.id,ayn1_0.title from article_yes_no ayn1_0 where ayn1_0.is_gone='N' and ayn1_0.id=?
   sql> update article_yes_no set is_gone='Y' where id=? and is_gone='N'
deleted : [1 | Y]

== 4. @SoftDelete on a @MappedSuperclass
create table note (deleted_at timestamp(6) with time zone, id bigint generated by default as identity, text varchar(255), primary key (id));
-- remove(note)
   sql> select n1_0.id,n1_0.text from note n1_0 where n1_0.deleted_at is null and n1_0.id=?
   sql> update note set deleted_at=localtimestamp where id=? and deleted_at is null
raw: [1 | true]
