yahoo / squidb

SquiDB is a SQLite database library for Android and iOS
https://github.com/yahoo/squidb/wiki
Apache License 2.0
1.31k stars 132 forks source link

Argument binding enhancements #217

Closed sbosley closed 7 years ago

sbosley commented 8 years ago

This PR contains several related refactors and enhancements.

The motivation for this change partially stems from the discussion in issue #215. By default, non-primitive types such as enums were bound to queries or other SQL statements as strings by calling toString() on the value before binding. However, in the case of e.g. enums, it is totally reasonable that the user might prefer to bind those values using the int ordinal, or some other proprietary identifier. This concept could also extend beyond enums to other high level wrapper types.

The main goal of this PR was to provide an API that would allow users to easily customize how non-primitive arguments are resolved before binding them to a SQL statement. The refactor in this PR consists of the following changes:

  1. Introduce the ArgumentResolver interface for controlling how higher-level types are resolved to primitives before binding. A DefaultArgumentResolver implementation replaces SqlUtils.resolveArgReferences. DefaultArgumentResolver is used when the user does not specify any other strategy (the common case). DefaultArgumentResolver is also extensible so users can easily introduce handling of new types without losing the default behavior for things like AtomicReferences.
  2. Introduce a CompileContext object that is passed to SQL builders when compiling them into a SQL string. This CompileContext object replaces the old compile(VersionCode) APIs, which are now deprecated. The CompileContext can hold a VersionCode, an ArgumentResolver, and an arbitrary map of user-defined key-value pairs for tracking custom metadata if needed. CompileContext is constructed via a builder, so we can imagine introducing new parameters to it without needing to break the compile() APIs again. The old compile(VersionCode) APIs simply construct a CompileContext with default parameters.
  3. A custom CompileContext can be supplied at the SquidDatabase level by overriding the new buildCompileContext hook. This hook allows users to modify the parameters of a CompileContext.Builder before it is returned to be used for compiling a Query or other SQL statement.
  4. Alternatively, a CompileContext can be set on an instance of Query or any other SQL statement. If set, this value would supersede the context provided by the SquidDatabase compiling the object, which would be ignored.
  5. Calling the supplied ArgumentResolver to resolve arguments now happens at the CompiledArgumentResolver level rather than the cursor factory level. This incidentally fixes an until-now unnoticed bug where higher-level arguments might not be correctly bound to non-query SQL statements.

So, for example, if a user wanted to handle enums by using ordinal() instead of name(), they would:

  1. Write a custom plugin for handling enum fields in model specs that would supersede the default enum handling.
  2. Extend DefaultArgumentResolver to unwrap enums using ordinal() instead of name(), and then supply this resolver in the buildCompileContext hook of their SquidDatabase.

Of course, this concept generalizes to any other high-level type, enum was just the most obvious example.


Other misc fixes:

jdkoren commented 7 years ago

LGTM