Giter VIP home page Giter VIP logo

safeql's Introduction

Why was SafeQL created?

SafeQL arose from the dissatisfaction in the limitations of existing ORM frameworks and query builder libraries. Often what works well in the beginning falls aport at scale. Painful examples of this are non-obvious N+1 queries that creep in and garbage collection pressure that eats memory and kills performance. Avoiding N+1 queries while simultaneously controlling eager/lazy loading is challenging and inefficient at best. Other frameworks are good at making simple things easier. They don't handle complex cases where it would be most beneficial and requires error-prone manual work.

SafeQL is the encapsulation of doing these complex and all-too-common things in a developer friendly and operationally safe way.

Features

  • Composable
  • Functional
  • Async-enabled
  • Batch-load multiple relations
  • No N+1 queries
  • Type-safe
  • Mix expression and SQL
  • Protect against SQL injection
  • Easy JOINs (foreign-keyed and ad-hoc)
  • Define in code or generate from existing schema sources
  • Rich (e.g. CTEs)
  • Handles IN (<empty>) / NOT IN (<empty>)

How does SafeQL work?

SafeQL combines several capabilities together to make great the default. Here they are from the highest level to the underlying building blocks:

  • functional composition of asynchronous results
  • asynchronous batch querying of entities and relationships
  • type-safe query composition
  • raw queries as typed expressions
  • safe SQL statements generated from expressions
  • composable templates with smart parameter binding
  • code generator from schema sources

Shut up and show me the code already

Querying a single table

Accounts accounts = Accounts.where("id >= ?", 1000).get();
  or
Accounts accounts = Accounts.where("id > :min_id", mapOf("min_id", 1000)).get();

Projects projects = accounts.loadProjects("updated_at >= ?", Instant.now().minusDays(30)).get();

The Accounts and Projects types are subclasses of List and List with additional methods.

The projects returned value of loadProjects can be discarded as each account in accounts aleady has its projects associated upon loading.

What with all the get() calls? Queries return async futures which can be funtionally chained. Calling get() or join() synchronously waits for the result.

Functional composition

Let's say we want to load some Accounts with some of their Projects and all Project members as well as all active Account admins.

CompletableFuture<Accounts> asyncAccounts = Accounts.where("id >= ?", 1000);

CompletableFuture<Member> asyncProjectMembers = Async.pipeline(asyncAccounts,
                              (accounts) -> accounts.loadProjects("updated_at >= ?", Instant.now().minusDays(30)),
                              (projects) -> projects.loadMembers());

CompletableFuture<Admin> asyncActiveAdmins = Async.pipeline(asyncAccounts,
                              (accounts) -> accounts.loadAdmins("active"));

Async.join(asyncProjectMembers, asyncActiveAdmins); // we don't need to wait for projects since loadMembers already does that

Now we synchronously have the accounts, admins, recent projects and their members all related in their respective associated collections. We can continue processing from here, or alternatively we could have continued to mix asynchronous processing logic:

Map<Project, Admin> notify = Async.combine(asyncProjects, asyncActiveAdmins,
                                 (projects, activeAdmins) -> {
                                     Map<Project, Admin> notify = new HashMap<>();
                                     // some logic here to select key projects and one admin
                                     // notify.put(project, admin);
                                     return notify;
                                 });

The pure functional elements can be extracted into testable classes and methods separately from the IO performed between them. The queries can also be independently tested with known data fixtures.

Development status

Done
[๐Ÿ‘] named the project
[๐Ÿ‘] no generated N+1 queries
[๐Ÿ‘] SQL injection protection

Core complete (needs test coverage)
[โœ”] handle literal predicate reductions
[โœ”] handle IN (:values) with empty values condition
[โœ”] async functional composition
[โœ”] base expressions
[โœ”] SQL schema
[โœ”] mix typed-expression and raw SQL
[โœ”] batch querying of entities' relations and sub-relations
[โœ”] SQL predicates
[โœ”] SQL expressions
[โœ”] composable base expressions and smart binding
[โœ”] rich SQL statement features (WITH CTE)
[โœ”] handle bind of :placeholder with Expr<?>

Working design (explore edge cases)
[โš™] SQL statements

In-progress
[โš ] local bind parameters on constructors
[โš ] composable query expressions and smart binding

Not started
[ ] string literals with embedded spaces in terms
[ ] statement transformation SELECT columns -> COUNT(a.*), COUNT(DISTINCT), EXISTS
[ ] aggregate functions (GROUP BY, HAVING)
[ ] rich SQL statement features (DISTINCT, DISTINCT ON, WINDOW, etc)
[ ] code generator

How to contribute

  • Write blog posts
  • Comment on existing Github issues
  • Submit a Github issue
  • Write tests
  • Fork the repo on GitHub, submit a PR

safeql's People

Contributors

dependabot[bot] avatar karmakaze avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

safeql's Issues

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.