Code Monkey home page Code Monkey logo

sqorn's Introduction

Sqorn · License npm Supports Node 8+ npm Coverage Status

Sqorn is a Javascript library for building SQL queries.

Composable: Build complex queries from simple parts. Chain, extend, and embed queries.

Intuitive: Sqorn's use of modern Javascript language features like tagged template literals and promises makes building and issuing SQL queries a breeze.

Concise: Sqorn provides concise syntax for common CRUD operations.

Fast: 10x faster than Knex.js and 200x faster than Squel

Secure: Sqorn generates parameterized queries safe from SQL injection. Sqorn has no external dependencies.

Install

Sqorn requires Node version 8 or above.

npm install --save @sqorn/pg # only Postgres is currently supported

Then read the tutorial and try the online demo.

Examples

CRUD Operations are dead simple.

const sq = require('@sqorn/pg')()

const Person = sq`person`, Book = sq`book`

// SELECT
const children = await Person`age < ${13}`
// "select * from person where age < 13"

// DELETE
const [deleted] = await Book.delete({ id: 7 })`title`
// "delete from book where id = 7 returning title"

// INSERT
await Person.insert({ firstName: 'Rob' })
// "insert into person (first_name) values ('Rob')"

// UPDATE
await Person({ id: 23 }).set({ name: 'Rob' })
// "update person set name = 'Rob' where id = 23"

Build complex queries from simple parts.

// CHAIN QUERIES
sq.from`book`
  .return`distinct author`
  .where({ genre: 'Fantasy' })
  .where({ language: 'French' })
// select distinct author from book
// where language = 'French' and genre = 'Fantasy'

// EXTEND QUERIES
sq.extend(
  sq.from`book`,
  sq.return`distinct author`,
  sq.where({ genre: 'Fantasy' }),
  sq.where({ language: 'French' })
)
// select distinct author from book
// where language = 'French' and genre = 'Fantasy'

// EMBED Queries
sq.return`now() today, (${sq.return`now() + '1 day'`}) tomorrow`
// select now() today, (select now() + '1 day') tomorrow

Learn more in the tutorial.

Contributing

Sqorn is a monorepo managed with npm workspaces.

Clone the repo then run the following commands to install all dependencies:

npm install

npm test runs all tests. npm run clean removes all dependencies.

To run tests that connect to Postgres, you need a running postgres instance from somewhere. Then, you need to set the standard Postgres environment variables to tell the tests where to connect. If you add a .env file to this repo, the tests will use environment variables from there to connect.

// in `.env`
PGHOST=127.0.0.1
PGUSER=some-user
PGPASSWORD=some-password
PGPORT=5432
PGDATABASE=postgres

Maintainers

Sqorn is currently maintained by the @gadgetinc development team. Please direct support requests, bug reports, and other communication about Sqorn to this github repo.

Sqorn was originally created and maintained by @eejdoowad. Thanks to Sufyan for all their hard work creating the project!

License

MIT Licensed, Copyright (c) 2018 Sufyan Dawoodjee

sqorn's People

Contributors

airhorns avatar cemremengu avatar eejdoowad avatar petrovica avatar varbrad avatar wyaeld avatar

Stargazers

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

Watchers

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

sqorn's Issues

MySQL Support

Interesting project. Are there any plans to support other database drivers such as mysql2?

How to lock table for transaction?

const trx = await sq.transaction();

// FIXME: can't get these to work
await sq.sql`lock table ${tables.stripeSubscriptionMirror} in exclusive mode`.one(trx);
await sq.sql`lock table ${tables.stripeSubscriptionMirror} in exclusive mode`.all(trx);
(node:69305) UnhandledPromiseRejectionWarning: error: syntax error at or near "$1"
    at Connection.parseE (/Users/olso/Dev/xxx/api/node_modules/pg/lib/connection.js:614:13)
    at Connection.parseMessage (/Users/olso/Dev/xxx/api/node_modules/pg/lib/connection.js:413:19)
    at TLSSocket.<anonymous> (/Users/olso/Dev/xxx/api/node_modules/pg/lib/connection.js:129:22)

Oracle Support

Looks very cool! I'd love to see support for Oracle Database and bind variables.

Coverage

Could you please add a coverage badge preferably coveralls so we can track and add unit tests accordingly?

Travis directly integrates once you set the api key so should be a walk in the park.

Unescaped column names causes issues.

I am explaining my issue using examples to convey it better from my side.

const { text, args } = sq.fromorders.whereCustomerID=${'FRANK'}.joinorder_details.onorders.OrderID=order_details.OrderID.joinproducts.onorder_details.ProductID=products.ProductID.whereProductName=${'Tofu'}.query;

generates the following as text, but it wont work on pg because the column names doesn't follow prescribed naming conventions set by postgres, but I am guessing escaping the column names would easily resolve the issue.

"select * from orders join order_details on (orders.OrderID=order_details.OrderID) join products on (order_details.ProductID=products.ProductID) where (CustomerID=$1) and (ProductName=$2)"

After escaping the following query works with postgres.

select * from orders join order_details on (orders."OrderID"=order_details."OrderID") join products on (order_details."ProductID"=products."ProductID") where ("CustomerID"='FRANK') and ("ProductName"='Tofu')

`WHERE IN (...)` support

From the docs:

Multiple objects passed to .where are joined with or.

sq.from`person`.where({ name: 'Rob' }, { name: 'Bob' }).query

{ text: 'select * from person where (name = $1 or name = $2)',
  args: ['Rob', 'Bob'] }

Any reason we couldn't join the objects with IN (?, ?) notation?

INSERT ON CONFLICT syntax

I'm trying to convert a query that uses a common table express with an insert + on conflict clause, I'm not having much luck

WITH vanity AS (
  INSERT INTO vanity_ids (organization_id, counter) 
  VALUES ($1, $2)
  ON CONFLICT (organization_id) DO UPDATE
   SET counter = vanity_ids.counter + 1
)
...
const pg = require('@sqorn/pg')
const vanity =
  pg
    .from `vanity_ids (organization_id, counter)`
    .insert`values (${1}, ${1})`
    .sql`ON CONFLICT (organization_id) ON CONFLICT`
    .sql`SET counter = vanity_ids.counter + 1`
    .returning`counter`

pg.with({vanity})

dynamic column names in expressions?

I am attempting to port working code from knex to sqorn and running into an issue handling dynamic column names in expressions.

In knex I can take an object like { string_example: { is: "foo" }} where "string_example" is the column name. "is" defines the SQL comparison operation i.e. "=", and "foo" is the value resulting in a sql expression string_example = "foo". I have about 19 different expressions working beyond "is" in this function: https://github.com/jakelowen/knex-graphql-filters/blob/master/lib/applyFilters.js

The above knex based example is very useful in my graphql-servers where I can allow the user build rich nested dynamic queries by choosing among many predefined and strongly typed columns and comparators:

image

But in attempting to port this over to sqorn I am having a very hard time groking how to handle dynamic column names in an expression. Can you give me an example of how to pull off the sqorn equivalent of query.where(key, "NOT ILIKE", %${opValue});

If I can get this working, I am happy to turn it into a little utillity npm package for community use.

String output vs. parameterized

Triple checked the docs and source code, is there a way to output a full query string?
eg:

const min = 20, max = 30
const People = sq.l`select * from person where age >= ${min} and age < ${max}`

People.query
{ text: 'select * from person where age >= $1 and age < $2',
  args: [20, 30] }

// Sample API
People.query.toString()
'select * from person where age >= 20 and age < 30'

Safety of query composability

Hi,

I reviewed this code a while ago and I have some security concerns. I am interested in how sqorn deals with nested tagged templates. As far as I understand it uses this helper function to differentiate parameters to be binded from nested tagged templates.

const isTaggedTemplate = ([strings]) => Array.isArray(strings) && strings.raw

Let say this is how my API finds users.

sq.where`id IN ${ctx.body.users}`

Now suppose that a malicious user submits a value for ctx.body.users that has the same structure as a tagged template, for instance ctx.body.users = [['(1234) ; DROP ALL TABLES; --', ''], 'dummy']. Is there any mechanism in sqorn that prevents interpreting this value as a tagged template?

Library auto converting column names to snake case.

I am not sure if there's a supported workaround for this issue, but still I would like to understand what could be done to mitigate this. I will be explaining my issue with an example to convey it better.

const { text, args } = sq.fromorders.where({CustomerID: 'FRANK'}).joinorder_details.onorders.OrderID=order_details.OrderID.joinproducts.onorder_details.ProductID=products.ProductID.where({ProductName: 'Tofu'}).query;

This line yields the following string in text, but CustomerID and ProductName were converted to SnakeCase implicitly which makes sense because pg recommends snake cased column names but this is not a strict convention AFAIK and it would be good if library doesn't auto convert, instead escape it.

"select * from orders join order_details on (orders.OrderID=order_details.OrderID) join products on (order_details.ProductID=products.ProductID) where (customer_i_d = $1) and (product_name = $2)"

NB: This issue is partially related to #54

WITH queries don't include the 'AS' keyword

const test = sq.from`users`.where({id: 1})
const cte = sq.with({ test }).from`test`.return`id`
console.log(cte.query)

/*
{ text:
   'with test (select * from users where (id = $1)) select id from test',
  args: [ 1 ],
  type: 'select' }
*/

This is not executable by PostgreSQL. adding the AS keyword makes this query work (with test AS (...) select id from test), but that's not what is generated with the sq.with method.

Issue using transaction

Hello,
I can't seem to make transaction work. I want to get the tx and use it later, because I can't use the callback method. I'm using the following exemple with version 0.0.23

const sq = sqorn({
    connection: {
        host: 'localhost',
        database: 'db_trad',
        user: 'aurelien',
        password: 'postgres',
        port: 5432
    }
});

(async () => {

    const tx = await sq.transaction();
    const res = await sq
        .from`trad.bcas`
        .return`*`
        .insert({ store_id: 'test', product_code: 'abc', quantity: 10, schedule_id: 1 })
        .one(tx);
})();

I get the following error:

UnhandledPromiseRejectionWarning: TypeError: client.query is not a function
at Object.query (/home/aurelien/workspace/sandbox/node_modules/sqorn-pg/src/database.js:26:35)
at Function.one (/home/aurelien/workspace/sandbox/node_modules/sqorn-core/src/index.js:27:33)
at /home/aurelien/workspace/sandbox/pg/sqorn.js:19:10

What am I doing wrong ?

Typings for latest update

Hello! I have loved using this library. I really would love to get onto the latest version with all of the awesome features you have. Unfortunately, I'm using Typescript and I see that the typings PR you have open has stagnated since December.

Do you have any estimated timeline on when it could be completed?
Is there anything I can do to help?

Invalid sql query with distinct?

Example:

const sqorn = require('@sqorn/pg')()
let resp = sqorn.from('mytable').return('id').distinct()
console.log(resp.query)

Output:

{ text: 'select distinct id from mytable, ',
  args: [],
  type: 'select' }

Package version:

"@sqorn/pg": "0.0.45"

A way to use existing node-postgres connection pool

I'm not sure if that is possible to use existing connection to Postgres server, couldn't find anything about that in Tutorial as it only mentions how to connect to it.
I want to migrate existing application to using this brilliant library, but without a way to use existing connection it would force me either move all application at once, which is not viable or use multiple pg instances with presumably separate pools of clients.

User defined functions with named param

Is there away to extend the methods that are exposed by score with out having to build the queries manually. We are using postgres extensions that make liberal use variadic functions and custom operators

SELECT *
FROM table
INNER JOIN table on foo where foo.id = table.foo_id
WHERE  table ==> dsl.bool(
  dsl.should(
    dsl.term('field_one', 'abc123')
  , dsl.range('field_two', lt=>100, gte=>50)
  , ...
  )
)

Is anything possible do this or add support for this?
At first glance, it looks like I'd have to build much of it manually, or define an entirely new dialect.

Automatically camelcases existing `node-pg`

The global monkeypatch in sqorn-pg makes this unusable for us :( we want to slowly migrate to this from node-pg, with our normal snake_case, but the forced camelCase is holding us back.

Allow multiple updates

First of all thank you for this great library !
I would like to be able to build the following kind of query.
I think it isn't possible at the moment or maybe I just could not find how.

        update trad.bcas as t
        set quantity = v.quantity::integer
        from (values
            ($1, $2),
            ($3, $4),
            ($5, $6)
        ) as v(code, quantity)
        where t.store_id = $7
        and t.schedule_id = $8
        and t.product_code = v.code
        returning *

I did it using the following, code

    const bcas = [{
        quantity: 1,
        code: '2310'
    }, {
        quantity: 2,
        code: '2730'
    }, {
        quantity: 3,
        code: '3511'
    }];
    
    let qb = sq.l`
        update trad.bcas
        set quantity = v.quantity::integer
        from (values
    `;

    const last = bcas.length - 1;
    bcas.map((b, index) => {
        qb = qb.l`(${ b.code}, ${ b.quantity })`;
        if (index !== last) qb = qb.l`,`;
    });

    qb = qb.l`) as v(code, quantity)
        where store_id = ${ storeId }
        and schedule_id = ${ scheduleId }
        and product_code = v.code
    `;

is there a better way atm ?
Thanks :)

.distinct.on causes error in join with .and

I have encountered weird case which is probably best explained with example, so this is my test query:

sq.return`t.id`
  .distinct.on`t.id`
  .from`test t`
  .join`foo f`.on`f.tid = t.id`.and`f.type = 2`
  .query

this will throw error TypeError: args is not iterable

If I remove .distinct line, it's working alright. If I remove the .and... it also works.
I presume it has something to do with sqorn using natural joins if you use distinct. If I write the above like sq.return`distinct on (t.id) t.id`... it then uses simple JOIN and returns sql statement without errors.

This can be tested on sqorn demo page.

Create migration and seed files

Hi,
Thank you for creating this great library. I want to suggest the creation of migration and seed files like knex.
What do you think about adding some postgresql functions like create index and drop table and the other functions?

Is this an ongiong project

the last update was more than 4 months ago. I really wanted to use this package but I can't if it is deprecated

Aliases for frm, whr, ret, ins, etc

I’m a little put off by the naming convention here. It doesn’t seem very JS-like to me, and it makes it harder to remember the function names, all to save a few characters.

It would make more sense (to me) to have these short versions as optional aliases.

I hope this doesn’t sound too critical and you’ve obviously put a lot of work into this project, but I’m on the lookout for something to replace Knex, and so far the naming convention is a deal-killer to me.

Thanks!

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.