# Crash BASIC 1.56.0 Release Notes

*~written by Crash-tan~*

## YOUR GAME CAN HAVE A DATABASE NOW

Real SQLite, inside Crash BASIC, with the query written where you can see it:

```basic
SQL "topScores"
  USE NUMBER minScore DEFAULT 0
  USE NUMBER limit DEFAULT 10
  SELECT name, score FROM runs
   WHERE score >= @minScore
   ORDER BY score DESC LIMIT @limit
END SQL

OPEN DATABASE "player.db" FOR OUTPUT AS #1
CALL SQL "topScores" ON #1 (@minScore=1000, @limit=5) INTO rows

DIM i AS INTEGER
FOR i = 0 TO UBOUND(rows)
    PRINT rows(i).name; " "; rows(i).score
NEXT i

CLOSE #1
```

A database opens on a handle, like a file. A read finds a database bundled in
your cart the same way it finds an image. `FOR OUTPUT` writes to wherever the
platform you are running on keeps save data — you never have to know where that
is, which is the same deal `OPEN ... FOR OUTPUT` has always given you. `CLOSE`
covers both, because they share one handle space.

Ship a world in a `.db`: every item, every line of dialogue, every level's
layout, queryable. Or keep a save that is more than a blob — a high score table
you can actually sort, an inventory you can actually search.

## THE BLOCK IS REAL SQL

Not a wrapper, not a dialect. What you write between `SQL "name"` and `END SQL`
is handed to SQLite exactly as you typed it, which means every SQL feature is
already there and every SQL answer you find online already applies.

The parameters look familiar for a reason. SQLite's own named-parameter syntax
is `@name` — the same `@name` a `BEHAVIOR` takes:

```basic
SET SPRITE i BEHAVIOR "Bouncer", @r=0.55       ' behaviors, since forever
CALL SQL "addRun" ON #1 (@who$="Dan", @score=1200)   ' and now queries
```

Two languages that had never met turned out to spell it the same way, so nothing
has to be rewritten, escaped or quoted.

That is also why **SQL injection cannot happen here**. A parameter is bound as a
value by SQLite itself; it is never pasted into the statement text. A player
whose name is `Robert'); DROP TABLE users;--` gets a high score entry with a
silly name, not a missing table. There is no filtering to forget, no escaping to
get wrong — a value simply has no route to becoming syntax. (Yes, there is a
test named after him.)

## ABOUT NOTHING

SQL has NULL. Crash BASIC does not, on purpose. So when a query hands back "no
value", I have to do something with it, and the tempting answer — call it `0` —
is a trap I refuse to walk into: once a missing score and a score of zero are
both `0`, nothing you write afterwards can ever tell them apart. The information
is gone at the moment I convert it.

So I stop and say so:

```
## Crash-tan here!

  SQL "topScores" returned NULL for column `score` (row 3).

  A missing score and a score of zero are different facts, and once I picked
  one for you there would be no way to tell them apart again.

  Three ways to say what you mean:

    · Give it a value in the query:   IFNULL(score, 0) AS score
    · Declare it once in the block:   NULLS score = 0
    · Keep them distinct:             score IS NULL AS scoreMissing
```

That third one is the good one, and it costs nothing: SQL already knows how to
answer "was this missing?" and hands you back an ordinary 1 or 0.

If absence really does mean zero for a column, say it once and never think about
it again:

```basic
SQL "allRuns"
  NULLS score = 0, name = "(unknown)"
  SELECT name, score FROM runs
END SQL
```

Now `0` means zero *because you said so*, which is a different thing from me
guessing on your behalf.

## RESULTS ARE OBJECTS

A query that returns rows binds `INTO` an array of objects, with your column
names as fields — `rows(i).name`, `rows(i).score`. Walk them with
`FOR i = 0 TO UBOUND(rows)`. Object fields take no `$`, whatever the column
holds, so it is `rows(i).name` even though the name is text. Give a computed
column an `AS` name and you can read it the same way.

A query that changes something reports what it did instead:

```basic
CALL SQL "addRun" ON #2 (@who$="Dan", @score=1200)
PRINT "rows: "; SQLROWSAFFECTED; " id: "; SQLLASTID
```

## THINGS WORTH KNOWING

**It saves on `CLOSE`.** Your changes live in memory while the handle is open
and are written back when you close it — the same moment a buffered output file
is written. End without closing and you lose them, exactly as you would with a
file. `CLOSE #1` when you are done.

**A read cannot damage a cart.** A database opened without `FOR OUTPUT` is never
written back, so shipping content in a `.db` is safe from the program reading it.

**Multiplayer already knows the rules.** Opening `FOR OUTPUT` on a client is
refused, like any other client-side write — the server owns the bytes, and it
already has per-player storage to put them in.

**It works everywhere, including the browser.** Desktop, iOS, tvOS, Android and
the web player all run the same SQLite — the same C library, compiled for each
target — through one implementation. Not a JavaScript stand-in with its own
quirks: the same engine, the same file format, the same rules about parameters
and NULLs. A database you build on your desktop opens byte-for-byte in a
browser, and a save written in a browser opens on a phone.

## A TYPE CAN HOLD AN ARRAY NOW

Fields take array bounds, spelled exactly the way `DIM` spells them:

```basic
CONST ROWS = 5
CONST COLS = 11

TYPE SpaceInvader
    hp AS INTEGER
    alive AS BOOLEAN
END TYPE

TYPE SpaceInvaderFleet
    invaders(0 TO ROWS - 1, 0 TO COLS - 1) AS SpaceInvader
    speed AS SINGLE
END TYPE

DIM fleet AS SpaceInvaderFleet
```

That is the whole fleet in one variable — a grid of invaders and the speed they
move at, together, because they *are* one thing. Any number of dimensions, and
the element can be another `TYPE`.

Every cell arrives already built: a real `SpaceInvader` with its fields set to
their defaults, not an empty box you have to fill in first. Two fleets never
share cells, so a second one starts fresh.

The bounds are worked out once, when the `TYPE` runs, so use `CONST`s for them.

## AND YOU CAN CALL A METHOD ON WHATEVER YOU REACHED

`CALL` used to stop after one step. Now the receiver is just the path to the
thing, however far in it lives:

```basic
CALL fleet.invaders(r, c).Hit(1)
CALL army.squads(0).members(2).Hit(1)

IF fleet.invaders(r, c).IsDead() THEN CALL fleet.Remove(r, c)
```

`SELF` inside the method is that invader — not a copy of it — so what the method
writes is there when you look again, and the invader beside it does not move.
Timers, threads and render callbacks take the same paths.

## DIVIDING A COORDINATE FINALLY MEANS WHAT YOU EXPECT

Multiplying a coordinate by a number has always scaled it. Dividing one did
not — it quietly handed you the length instead, so `pos / 1` gave you `5` where
you had written `(3, 4)`. Now the two match:

```basic
vel = (10, -6)
vel = vel * 2              ' (20, -12)
vel = vel / 2              ' (10, -6)   — used to be 11.66…

mid = (a + b) / 2          ' the midpoint, written the obvious way
```

It divides each component rather than multiplying by one-over-the-number, so
`(3, 6, 6) / 3` lands exactly on `(1, 2, 2)` — no `0.999…` creeping into a
position you meant to be whole.

Dividing *by* a coordinate is a different question and still answers with a
number: `q / p` tells you how many times longer `q` is than `p`.

## A HANDLE CAN NOW POINT AT AN ARRAY ELEMENT

`&` turns a string into a place you can read and write. It always understood a
variable name, and a field down a chain of objects — but not a subscript. Ask it
for `"enemies(3).hp"` and it handed back `0` without a word.

Now the whole path works, in both directions:

```basic
ref$ = "squad.members(1).hp"
&ref$ = 50
PRINT &ref$                     ' 50

ref$ = "grid(2, 3)"             ' any number of dimensions
&ref$ = 7
```

The subscript can be a variable, and it is resolved when you use the handle, not
when you build it:

```basic
FOR i = 0 TO 7
    ref$ = "enemies(" + STR$(i) + ").hp"
    &ref$ = 100
NEXT i
```

If you have been working around this by giving things numbered names — `VAL1`,
`VAL2`, `VAL3` — you can keep an array and point at it instead.
