flow: simplify better-sqlite3 bound parameters (#869)

Summary:
The actual constraints for bound parameters are too complicated to
express within Flow. This commit changes the type definitions from one
approximation to another, simpler one. Neither approximation is likely
to cause many problems in practice, either in terms of spurious errors
or spurious lacks of error. (The failure mode for the new formulation is
having multiple dictionaries of binding values, which would pass Flow
but quickly raise a `TypeError` at runtime.)

The reason for the change is that it makes the method definitions
considerably simpler, in a way that is likely to avoid other problems
with Flow. In particular, this removes method overloads and the need for
parameter disambiguation.

I fix a typo while in the area.

Test Plan:
Note that the following file typechecks:

```js
// @flow
import Database from "better-sqlite3";
const db = new Database(":memory:");
const stmt = db.prepare("BEGIN"); // SQL text doesn't matter

stmt.run();
stmt.run(null, 2, "three", new Buffer(Array(4)));
stmt.run(+false);
stmt.run(1, {dos: 2}, 3); // the binding dictionary can go anywhere
stmt.run({a: 1}, {b: 2}); // this will fail at runtime (TypeError)

// $ExpectFlowError
stmt.run(false); // booleans cannot be bound
// $ExpectFlowError
stmt.run({x: {y: "z"}}); // named parameters are not recursive
```

All but the last two success cases (lines 9 and 10) would also have
passed before this change.

wchargin-branch: flow-better-sqlite3-bound-parameters-simplify
This commit is contained in:
William Chargin 2018-09-20 11:31:26 -07:00 committed by GitHub
parent e572551cd8
commit 7702bb2e1d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 17 additions and 40 deletions

View File

@ -41,12 +41,13 @@ declare type bettersqlite3$Database$RegisterOptions = {
+safeIntegers?: boolean +safeIntegers?: boolean
}; };
// For functions that accept bound parameters, we permit an optional // Functions that accept bound parameters take positional parameters,
// binding dictionary, followed by zero or more bound values. In // named parameters (passed as an object), or a combination of the two.
// reality, better-sqlite3 is more flexible than this: the binding // The named parameters can be placed anywhere in the argument list, but
// dictionary can go anywhere among the arguments. We can't express this // must appear at most once. We can't express this constraint, so we
// type, though, and it is strange to use both named and numbered // permit any argument to be either a simple value or a dictionary of
// parameters at all, so we accept this concession. // values. In the case that a user provides multiple binding
// dictionaries, better-sqlite3 will fail fast with a TypeError.
// //
// Also note that better-sqlite3 permits binding `Integer.IntLike` from // Also note that better-sqlite3 permits binding `Integer.IntLike` from
// npm/integer, not just `number`, but we don't have those typedefs. For // npm/integer, not just `number`, but we don't have those typedefs. For
@ -55,6 +56,9 @@ declare type bettersqlite3$BoundValue = number | string | Buffer | null;
declare type bettersqlite3$BindingDictionary = { declare type bettersqlite3$BindingDictionary = {
+[string]: bettersqlite3$BoundValue +[string]: bettersqlite3$BoundValue
}; };
declare type bettersqlite3$BoundParameter =
| bettersqlite3$BoundValue
| bettersqlite3$BindingDictionary;
declare class bettersqlite3$Statement { declare class bettersqlite3$Statement {
+memory: boolean; +memory: boolean;
@ -63,32 +67,12 @@ declare class bettersqlite3$Statement {
+open: boolean; +open: boolean;
+inTransaction: boolean; +inTransaction: boolean;
run(...params: bettersqlite3$BoundValue[]): bettersqlite3$RunResult; run(...params: bettersqlite3$BoundParameter[]): bettersqlite3$RunResult;
run( get(...params: bettersqlite3$BoundParameter[]): any;
namedParams: bettersqlite3$BindingDictionary, all(...params: bettersqlite3$BoundParameter[]): any[];
...params: bettersqlite3$BoundValue[] iterate(...params: bettersqlite3$BoundParameter[]): Iterator<any>;
): bettersqlite3$RunResult;
get(...params: bettersqlite3$BoundValue[]): any;
get(
namedParams: bettersqlite3$BindingDictionary,
...params: bettersqlite3$BoundValue[]
): any;
all(...params: bettersqlite3$BoundValue[]): any[];
all(
namedParams: bettersqlite3$BindingDictionary,
...params: bettersqlite3$BoundValue[]
): any[];
iterate(...params: bettersqlite3$BoundValue[]): Iterator<any>;
iterate(
namedParams: bettersqlite3$BindingDictionary,
...params: bettersqlite3$BoundValue[]
): Iterator<any>;
pluck(toggleState?: boolean): this; pluck(toggleState?: boolean): this;
bind(...params: bettersqlite3$BoundValue[]): this; bind(...params: bettersqlite3$BoundParameter[]): this;
bind(
namedParams: bettersqlite3$BindingDictionary,
...params: bettersqlite3$BoundValue[]
): this;
safeIntegers(toggleState?: boolean): this; safeIntegers(toggleState?: boolean): this;
} }
@ -98,15 +82,7 @@ declare class bettersqlite3$Transaction {
constructor(db: bettersqlite3$Database, sources: string[]): void; constructor(db: bettersqlite3$Database, sources: string[]): void;
run(...params: any[]): bettersqlite3$RunResult; run(...params: any[]): bettersqlite3$RunResult;
run(
namedParams: bettersqlite3$BindingDictionary,
...params: bettersqlite3$BoundValue[]
): bettersqlite3$RunResult;
bind(...params: any[]): this; bind(...params: any[]): this;
bind(
namedParams: bettersqlite3$BindingDictionary,
...params: bettersqlite3$BoundValue[]
): this;
safeIntegers(toggleState?: boolean): this; safeIntegers(toggleState?: boolean): this;
} }
@ -128,7 +104,8 @@ declare module "better-sqlite3" {
declare export type Database$ConstructorOptions = bettersqlite3$Database$ConstructorOptions; declare export type Database$ConstructorOptions = bettersqlite3$Database$ConstructorOptions;
declare export type Database$RegisterOptions = bettersqlite3$Database$RegisterOptions; declare export type Database$RegisterOptions = bettersqlite3$Database$RegisterOptions;
declare export type BoundValue = bettersqlite3$BoundValue; declare export type BoundValue = bettersqlite3$BoundValue;
declare export type BidningDictionary = bettersqlite3$BindingDictionary; declare export type BindingDictionary = bettersqlite3$BindingDictionary;
declare export type BoundParameter = bettersqlite3$BoundParameter;
declare export type Statement = bettersqlite3$Statement; declare export type Statement = bettersqlite3$Statement;
declare export type Transaction = bettersqlite3$Transaction; declare export type Transaction = bettersqlite3$Transaction;
declare export type RunResult = bettersqlite3$RunResult; declare export type RunResult = bettersqlite3$RunResult;