The replies got trashed pretty badly in the responses.
When you say: "Agree it's better to reuse existing syntax than invent new
syntax."

Which syntax are you referring to?

Patrick


On Mon, Aug 22, 2022 at 1:36 AM Avi Kivity via dev <dev@cassandra.apache.org>
wrote:

> Agree it's better to reuse existing syntax than invent new syntax.
>
> On 8/21/22 16:52, Konstantin Osipov wrote:
> > * Avi Kivity via dev <dev@cassandra.apache.org> [22/08/14 15:59]:
> >
> > MySQL supports SELECT <expr_list> INTO <var_list> FROM ... WHERE
> > ...
> >
> > PostgreSQL supports pretty much the same syntax.
> >
> > Maybe instead of LET use the ANSI/MySQL/PostgreSQL DECLARE var TYPE and
> > MySQL/PostgreSQL SELECT ... INTO?
> >
> >> On 14/08/2022 01.29, Benedict Elliott Smith wrote:
> >>> 
> >>> I’ll do my best to express with my thinking, as well as how I would
> >>> explain the feature to a user.
> >>>
> >>> My mental model for LET statements is that they are simply SELECT
> >>> statements where the columns that are selected become variables
> >>> accessible anywhere in the scope of the transaction. That is to say,
> you
> >>> should be able to run something like s/LET/SELECT and
> >>> s/([^=]+)=([^,]+)(,|$)/\2 AS \1\3/g on the columns of a LET statement
> >>> and produce a valid SELECT statement, and vice versa. Both should
> >>> perform identically.
> >>>
> >>> e.g.
> >>> SELECT pk AS key, v AS value FROM table
> >>>
> >>> =>
> >>> LET key = pk, value = v FROM table
> >>
> >> "=" is a CQL/SQL operator. Cassandra doesn't support it yet, but SQL
> >> supports selecting comparisons:
> >>
> >>
> >> $ psql
> >> psql (14.3)
> >> Type "help" for help.
> >>
> >> avi=# SELECT 1 = 2, 3 = 3, NULL = NULL;
> >>   ?column? | ?column? | ?column?
> >> ----------+----------+----------
> >>   f        | t        |
> >> (1 row)
> >>
> >>
> >> Using "=" as a syntactic element in LET would make SELECT and LET
> >> incompatible once comparisons become valid selectors. Unless they become
> >> mandatory (and then you'd write "LET q = a = b" if you wanted to select
> a
> >> comparison).
> >>
> >>
> >> I personally prefer the nested query syntax:
> >>
> >>
> >>      LET (a, b, c) = (SELECT foo, bar, x+y FROM ...);
> >>
> >>
> >> So there aren't two similar-but-not-quite-the-same syntaxes. SELECT is
> >> immediately recognizable by everyone as a query, LET is not.
> >>
> >>
> >>> Identical form, identical behaviour. Every statement should be directly
> >>> translatable with some simple text manipulation.
> >>>
> >>> We can then make this more powerful for users by simply expanding
> SELECT
> >>> statements, e.g. by permitting them to declare constants and tuples in
> >>> the column results. In this scheme LET x = * is simply syntactic sugar
> >>> for LET x = (pk, ck, field1, …) This scheme then supports options 2, 4
> >>> and 5 all at once, consistently alongside each other.
> >>>
> >>> Option 6 is in fact very similar, but is strictly less flexible for the
> >>> user as they have no way to declare multiple scalar variables without
> >>> scoping them inside a tuple.
> >>>
> >>> e.g.
> >>> LET key = pk, value = v FROM table
> >>> IF key > 1 AND value > 1 THEN...
> >>>
> >>> =>
> >>> LET row = SELECT pk AS key, v AS value FROM table
> >>> IF row.key > 1 AND row.value > 1 THEN…
> >>>
> >>> However, both are expressible in the existing proposal, as if you
> prefer
> >>> this naming scheme you can simply write
> >>>
> >>> LET row = (pk AS key, v AS value) FROM table
> >>> IF row.key > 1 AND row.value > 1 THEN…
> >>>
> >>> With respect to auto converting single column results to a scalar, we
> do
> >>> need a way for the user to say they care whether the row was null or
> the
> >>> column. I think an implicit conversion here could be surprising.
> However
> >>> we could implement tuple expressions anyway and let the user explicitly
> >>> declare v as a tuple as Caleb has suggested for the existing proposal
> as
> >>> well.
> >>>
> >>> Assigning constants or other values not selected from a table would
> also
> >>> be a little clunky:
> >>>
> >>> LET v1 = someFunc(), v2 = someOtherFunc(?)
> >>> IF v1 > 1 AND v2 > 1 THEN…
> >>>
> >>> =>
> >>> LET row = SELECT someFunc() AS v1, someOtherFunc(?) AS v2
> >>> IF row.v1 > 1 AND row.v2 > 1 THEN...
> >>>
> >>> That said, the proposals are /close/ to identical, it is just slightly
> >>> more verbose and slightly less flexible.
> >>>
> >>> Which one would be most intuitive to users is hard to predict. It might
> >>> be that Option 6 would be slightly easier, but I’m unsure if there
> would
> >>> be a huge difference.
> >>>
> >>>
> >>>> On 13 Aug 2022, at 16:59, Patrick McFadin <pmcfa...@gmail.com> wrote:
> >>>>
> >>>> I'm really happy to see CEP-15 getting closer to a final
> >>>> implementation. I'm going to walk through my reasoning for your
> >>>> proposals wrt trying to explain this to somebody new.
> >>>>
> >>>> Looking at all the options, the first thing that comes up for me is
> >>>> the Cassandra project's complicated relationship with NULL.  We have
> >>>> prior art with EXISTS/NOT EXISTS when creating new tables. IS
> >>>> NULL/IS NOT NULL is used in materialized views similarly to
> >>>> proposals 2,4 and 5.
> >>>>
> >>>> CREATE MATERIALIZED VIEW [ IF NOT EXISTS ] [keyspace_name.]view_name
> >>>>    AS SELECT [ (column_list) ]
> >>>>    FROM [keyspace_name.]table_name
> >>>>    [ WHERE column_name IS NOT NULL
> >>>>    [ AND column_name IS NOT NULL ... ] ]
> >>>>    [ AND relation [ AND ... ] ]
> >>>>    PRIMARY KEY ( column_list )
> >>>>    [ WITH [ table_properties ]
> >>>>    [ [ AND ] CLUSTERING ORDER BY (cluster_column_name order_option) ]
> ] ;
> >>>>
> >>>>   Based on that, I believe 1 and 3 would just confuse users, so -1 on
> >>>> those.
> >>>>
> >>>> Trying to explain the difference between row and column operations
> >>>> with LET, I can't see the difference between a row and column in #2.
> >>>>
> >>>> #4 introduces a boolean instead of column names and just adds more
> >>>> syntax.
> >>>>
> >>>> #5 is verbose and, in my opinion, easier to reason when writing a
> >>>> query. Thinking top down, I need to know if these exact rows and/or
> >>>> column values exist before changing them, so I'll define them first.
> >>>> Then I'll iterate over the state I created in my actual changes so I
> >>>> know I'm changing precisely what I want.
> >>>>
> >>>> #5 could use a bit more to be clearer to somebody who doesn't write
> >>>> CQL queries daily and wouldn't require memorizing subtle
> >>>> differences. It should be similar to all the other syntax, so
> >>>> learning a little about CQL will let you move into more without
> >>>> completely re-learning the new syntax.
> >>>>
> >>>> So I propose #6)
> >>>> BEGIN TRANSACTION
> >>>> LET row1 = SELECT * FROM ks.tbl WHERE k=0 AND c=0; <-- * selects all
> >>>> columns
> >>>> LET row2 = SELECT v FROM ks.tbl WHERE k=1 AND c=0;
> >>>>    SELECT row1, row2
> >>>> IF row1 IS NULL AND row2.v = 3 THEN
> >>>>    INSERT INTO ks.tbl (k, c, v) VALUES (0, 0, 1);
> >>>> END IF
> >>>> COMMIT TRANSACTION
> >>>>
> >>>> I added the SELECT in the LET just so it's straightforward, you are
> >>>> reading, and it's just like doing a regular select, but you are
> >>>> assigning it to a variable.
> >>>>
> >>>> I removed the confusing 'row1.v'and replaced it with 'row1'I can't
> >>>> see why you would need the '.v'vs having the complete variable I
> >>>> created in the statement above.
> >>>>
> >>>> EOL
> >>>>
> >>>> Patrick
> >>>>
> >>>> On Thu, Aug 11, 2022 at 1:37 PM Caleb Rackliffe
> >>>> <calebrackli...@gmail.com> wrote:
> >>>>
> >>>>      ...and one more option...
> >>>>
> >>>>      5.) Introduce tuple assignments, removing all ambiguity around
> >>>>      row vs. column operations.
> >>>>
> >>>>      BEGIN TRANSACTION
> >>>>        LET row1 = * FROM ks.tbl WHERE k=0 AND c=0; <-- * selects all
> >>>>      columns
> >>>>        LET row2 = (v) FROM ks.tbl WHERE k=1 AND c=0;
> >>>>      SELECT row1.v, row2.v
> >>>>        IF row1 IS NULL AND row2.v = 3 THEN
> >>>>          INSERT INTO ks.tbl (k, c, v) VALUES (0, 0, 1);
> >>>>        END IF
> >>>>      COMMIT TRANSACTION
> >>>>
> >>>>
> >>>>
> >>>>      On Thu, Aug 11, 2022 at 12:55 PM Caleb Rackliffe
> >>>>      <calebrackli...@gmail.com> wrote:
> >>>>
> >>>>          via Benedict, here is a 4th option:
> >>>>
> >>>>          4.) Similar to #2, but don't rely on the key element being
> NULL.
> >>>>
> >>>>          If the read returns no result, x effectively becomes NULL.
> >>>>          Otherwise, it remains true/NOT NULL.
> >>>>
> >>>>          BEGIN TRANSACTION
> >>>>            LET x = true FROM ks.tbl WHERE k=0 AND c=0;
> >>>>            LET row2_v = v FROM ks.tbl WHERE k=1 AND c=0;
> >>>>            SELECT x, row2_v
> >>>>            IF x IS NULL AND row2_v = 3 THEN
> >>>>              INSERT INTO ks.tbl (k, c, v) VALUES (0, 0, 1);
> >>>>            END IF
> >>>>          COMMIT TRANSACTION
> >>>>
> >>>>          On Thu, Aug 11, 2022 at 12:12 PM Caleb Rackliffe
> >>>>          <calebrackli...@gmail.com> wrote:
> >>>>
> >>>>              Hello again everyone!
> >>>>
> >>>>              I've been working on a prototype
> >>>>              <https://issues.apache.org/jira/browse/CASSANDRA-17719
> > in
> >>>>              CASSANDRA-17719 for a grammar that roughly corresponds to
> >>>>              what we've agreed on in this thread. One thing that isn't
> >>>>              immediately obvious to me is how the LET syntax handles
> >>>>              cases where we want to check for the plain existence of a
> >>>>              row in IF. For example, in this hybrid of the originally
> >>>>              proposed syntax and something more like what we've agreed
> >>>>              on (and the RETURNING just to distinguish between that
> >>>>              and SELECT), this could be pretty straightforward:
> >>>>
> >>>>              BEGIN TRANSACTION
> >>>>                SELECT v FROM ks.tbl WHERE k=0 AND c=0 AS row1;
> >>>>                SELECT v FROM ks.tbl WHERE k=1 AND c=0 AS row2;
> >>>>                RETURNING row1.v, row2.v
> >>>>                IF row1 NOT EXISTS AND row2.v = 3 THEN
> >>>>                  INSERT INTO ks.tbl (k, c, v) VALUES (0, 0, 1);
> >>>>                END IF
> >>>>              COMMIT TRANSACTION
> >>>>
> >>>>              The NOT EXISTS operator has row1 to work with. One the
> >>>>              other hand, w/ the LET syntax and no naming of reads,
> >>>>              it's not clear what the best solution would be. Here are
> >>>>              a few possibilities:
> >>>>
> >>>>              1.) Provide a few built-in functions that operate on a
> >>>>              whole result row. If we assume a SQL style IS NULL and IS
> >>>>              NOT NULL (see my last post here) for operations on
> >>>>              particular columns, this probably eliminates the need for
> >>>>              EXISTS/NOT EXISTS as well.
> >>>>
> >>>>              BEGIN TRANSACTION
> >>>>                LET row1_missing = notExists() FROM ks.tbl WHERE k=0
> >>>>              AND c=0;
> >>>>                LET row2_v = v FROM ks.tbl WHERE k=1 AND c=0;
> >>>>                SELECT row1_missing, row2_v
> >>>>                IF row1_missing AND row2_v = 3 THEN
> >>>>                  INSERT INTO ks.tbl (k, c, v) VALUES (0, 0, 1);
> >>>>                END IF
> >>>>              COMMIT TRANSACTION
> >>>>
> >>>>              2.) Assign and check the first primary key element to
> >>>>              determine whether the row exists.
> >>>>
> >>>>              BEGIN TRANSACTION
> >>>>                LET row1_k = k FROM ks.tbl WHERE k=0 AND c=0;
> >>>>                LET row2_v = v FROM ks.tbl WHERE k=1 AND c=0;
> >>>>                SELECT row1_k, row2_v
> >>>>                IF row1_k IS NULL AND row2_v = 3 THEN
> >>>>                  INSERT INTO ks.tbl (k, c, v) VALUES (0, 0, 1);
> >>>>                END IF
> >>>>              COMMIT TRANSACTION
> >>>>
> >>>>              3.) Reconsider the LET concept toward something that
> >>>>              allows us to explicitly name our reads again.
> >>>>
> >>>>              BEGIN TRANSACTION
> >>>>                WITH (SELECT v FROM ks.tbl WHERE k=0 AND c=0) AS row1;
> >>>>                WITH (SELECT v FROM ks.tbl WHERE k=1 AND c=0) AS row2;
> >>>>                SELECT row1.v, row2.v
> >>>>                IF row1 NOT EXISTS AND row2.v = 3 THEN
> >>>>                  INSERT INTO ks.tbl (k, c, v) VALUES (0, 0, 1);
> >>>>                END IF
> >>>>              COMMIT TRANSACTION
> >>>>
> >>>>              I don't have a strong affinity for any of these, although
> >>>>              #1 seems the most awkward.
> >>>>
> >>>>              Does anyone have any other alternatives? Preference for
> >>>>              one of the above options?
> >>>>
> >>>>              Thanks!
> >>>>
> >>>>              On Fri, Jul 22, 2022 at 11:21 AM Caleb Rackliffe
> >>>>              <calebrackli...@gmail.com> wrote:
> >>>>
> >>>>                  Avi brought up an interesting point around NULLness
> >>>>                  checking inCASSANDRA-17762
> >>>>                  <
> https://issues.apache.org/jira/browse/CASSANDRA-17762>...
> >>>>
> >>>>                      In SQL, any comparison with NULL is NULL, which
> >>>>                      is interpreted as FALSE in a condition. To test
> >>>>                      for NULLness, you use IS NULL or IS NOT NULL. But
> >>>>                      LWT uses IF col = NULL as a NULLness test. This
> >>>>                      is likely to confuse people coming from SQL and
> >>>>                      hamper attempts to extend the dialect.
> >>>>
> >>>>
> >>>>                  We can leave that Jira open to address what to do in
> >>>>                  the legacy LWT case, but I'd support a SQL-congruent
> >>>>                  syntax here (IS NULL or IS NOT NULL), where we have
> >>>>                  something closer to a blank slate.
> >>>>
> >>>>                  Thoughts?
> >>>>
> >>>>                  On Thu, Jun 30, 2022 at 6:25 PM Abe Ratnofsky
> >>>>                  <a...@aber.io> wrote:
> >>>>
> >>>>                      The new syntax looks great, and I’m really
> >>>>                      excited to see this coming together.
> >>>>
> >>>>                      One piece of feedback on the proposed syntax is
> >>>>                      around the use of “=“ as a declaration in
> >>>>                      addition to its current use as an equality
> >>>>                      operator in a WHERE clause and an assignment
> >>>>                      operator in an UPDATE:
> >>>>
> >>>>                          BEGIN TRANSACTION
> >>>>                            LET car_miles = miles_driven,
> >>>>                          car_is_running = is_running FROM cars WHERE
> >>>>                          model=’pinto’
> >>>>                            LET user_miles = miles_driven FROM users
> >>>>                          WHERE name=’blake’
> >>>>                            SELECT something else from some other table
> >>>>                            IF NOT car_is_running THEN ABORT
> >>>>                            UPDATE users SET miles_driven = user_miles
> >>>>                          + 30 WHERE name='blake';
> >>>>                            UPDATE cars SET miles_driven = car_miles +
> >>>>                          30 WHERE model='pinto';
> >>>>                          COMMIT TRANSACTION
> >>>>
> >>>>                      This is supported in languages like PL/pgSQL, but
> >>>>                      in a normal SQL query kind of local declaration
> >>>>                      is often expressed as an alias (SELECT col AS
> >>>>                      new_col), subquery alias (SELECT col) t, or
> >>>>                      common table expression (WITH t AS (SELECT col)).
> >>>>
> >>>>                      Here’s an example of an alternative to the
> >>>>                      proposed syntax that I’d find more readable:
> >>>>
> >>>>                          BEGIN TRANSACTION
> >>>>                            WITH car_miles, car_is_running AS (SELECT
> >>>>                          miles_driven, is_running FROM cars WHERE
> >>>>                          model=’pinto’),
> >>>>                          user_miles AS (SELECT miles_driven FROM users
> >>>>                          WHERE name=’blake’)
> >>>>                            IF NOT car_is_running THEN ABORT
> >>>>                            UPDATE users SET miles_driven = user_miles
> >>>>                          + 30 WHERE name='blake';
> >>>>                          UPDATE cars SET miles_driven = car_miles + 30
> >>>>                          WHERE model='pinto';
> >>>>                          COMMIT TRANSACTION
> >>>>
> >>>>                      There’s also the option of naming the transaction
> >>>>                      like a subquery, and supporting LET via AS (this
> >>>>                      one I’m less sure about but wanted to propose
> >>>>                      anyway):
> >>>>
> >>>>                          BEGIN TRANSACTION t1
> >>>>                            SELECT miles_driven AS t1.car_miles,
> >>>>                          is_running AS t1.car_is_running FROM cars
> >>>>                          WHERE model=’pinto’;
> >>>>                            SELECT miles_driven AS t1.user_miles FROM
> >>>>                          users WHERE name=’blake’;
> >>>>                            IF NOT car_is_running THEN ABORT
> >>>>                            UPDATE users SET miles_driven = user_miles
> >>>>                          + 30 WHERE name='blake';
> >>>>                            UPDATE cars SET miles_driven = car_miles +
> >>>>                          30 WHERE model='pinto';
> >>>>                          COMMIT TRANSACTION
> >>>>
> >>>>                      This also has the benefit of resolving ambiguity
> >>>>                      in case of naming conflicts with existing (or
> >>>>                      future) column names.
> >>>>
> >>>>                      --
> >>>>                      Abe
> >>>>
>
>

Reply via email to