Skip to content
This repository has been archived by the owner on Mar 24, 2024. It is now read-only.

Commit

Permalink
Merge pull request #29 from eejdoowad/join2
Browse files Browse the repository at this point in the history
Join Support
  • Loading branch information
eejdoowad committed Sep 20, 2018
2 parents faa7fb0 + cf73601 commit e750245
Show file tree
Hide file tree
Showing 13 changed files with 367 additions and 41 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Expand Up @@ -15,4 +15,4 @@ install:
- npm run bootstrap

script:
- npm run test -- --coverage --coverageReporters=text-lcov | coveralls
- (npm run test -- --coverage --coverageReporters=text-lcov | ./node_modules/.bin/coveralls; exit ${PIPESTATUS[0]})
2 changes: 1 addition & 1 deletion packages/sqorn-sql/src/clauses/value.js
Expand Up @@ -2,7 +2,7 @@ const { isTaggedTemplate, buildTaggedTemplate, snakeCase } = require('../util')

module.exports = ctx => {
const { columns, values } = inserts(ctx)
return '(' + columns + ') values ' + values
return `(${columns})${ctx.separator}values ${values}`
}

const inserts = ctx => {
Expand Down
2 changes: 1 addition & 1 deletion packages/sqorn-sql/src/clauses/where.js
Expand Up @@ -6,6 +6,6 @@ const { conditions } = require('../util')
// b. object argument
module.exports = ctx => {
if (ctx.whr.length === 0) return
const txt = conditions(ctx)
const txt = conditions(ctx, ctx.whr)
return txt && 'where ' + txt
}
163 changes: 133 additions & 30 deletions packages/sqorn-sql/src/methods.js
@@ -1,34 +1,37 @@
/** Initial ctx value */
const newContextCreator = ({ parameter }) => ({ arg = [] } = {}) => ({
type: 'select',
express: 'from',
separator: ' ',
sql: [],
frm: [],
whr: [],
ret: [],
ins: [],
set: [],
arg,
parameter
})
const newContextCreator = ({ parameter }) => ({ arg = [] } = {}) => {
const whr = []
return {
// query type: 'sql' | 'select' | 'delete' | 'insert' | 'update'
type: 'select',
// express syntax status: 'from' | 'where' | 'return'
express: 'from',
// associates calls to .and and .or with calls to .where, .on, or .having
target: whr,
// next join target
nextJoin: { join: 'inner' },
// current join target, set to ctx.nextJoin on call to .join
join: undefined,
// string used to join clauses
separator: ' ',
// clause data, e.g. sq.from() modifies ctx.frm
sql: [],
frm: [],
whr,
ret: [],
ins: [],
set: [],
// parameterized query arguments, initialized to [] but subqueries
// inherit parent query's arg
arg,
// function that parameterizes an argument by adding it to ctx.arg then
// returning the result text, e.g. '$1', '$2', ..., or '?' for mysql
parameter
}
}

/** Query building methods */
const methods = [
{
name: 'delete',
getter: true,
updateContext: ctx => {
ctx.type = 'delete'
}
},
{
name: 'recursive',
getter: true,
updateContext: ctx => {
ctx.recursive = true
}
},
{
name: 'l',
updateContext: (ctx, args) => {
Expand All @@ -52,19 +55,46 @@ const methods = [
{
name: 'with',
updateContext: (ctx, args) => {
throw Error('Unimplemented')
ctx.with.push(args)
}
},
{
name: 'recursive',
getter: true,
updateContext: ctx => {
throw Error('Unimplemented')
}
},
{
name: 'from',
updateContext: (ctx, args) => {
ctx.frm.push({ type: 'from', args })
ctx.frm.push({ args })
}
},
{
name: 'where',
updateContext: (ctx, args) => {
ctx.whr.push(args)
ctx.whr.push({ type: 'and', args })
ctx.target = ctx.whr
}
},
{
name: 'and',
updateContext: (ctx, args) => {
ctx.target.push({ type: 'and', args })
}
},
{
name: 'or',
updateContext: (ctx, args) => {
ctx.target.push({ type: 'or', args })
}
},
{
name: 'wrap',
updateContext: (ctx, args) => {
throw Error('Unimplemented')
}
},
{
Expand Down Expand Up @@ -103,6 +133,79 @@ const methods = [
ctx.off = args
}
},
{
name: 'join',
updateContext: (ctx, args) => {
ctx.join = ctx.nextJoin
ctx.join.args = args
ctx.nextJoin = { join: 'inner' }
ctx.frm.push(ctx.join)
}
},
{
name: 'left',
getter: true,
updateContext: ctx => {
ctx.nextJoin.join = 'left'
}
},
{
name: 'right',
getter: true,
updateContext: ctx => {
ctx.nextJoin.join = 'right'
}
},
{
name: 'full',
getter: true,
updateContext: ctx => {
ctx.nextJoin.join = 'full'
}
},
{
name: 'cross',
getter: true,
updateContext: ctx => {
ctx.nextJoin.join = 'cross'
}
},
{
name: 'inner',
getter: true,
updateContext: ctx => {
ctx.nextJoin.join = 'inner'
}
},
{
name: 'on',
updateContext: (ctx, args) => {
const { join } = ctx
if (join.on) {
join.on.push({ type: 'and', args })
} else {
ctx.target = join.on = [{ type: 'and', args }]
}
}
},
{
name: 'using',
updateContext: (ctx, args) => {
const { join } = ctx
if (join.using) {
join.using.push(args)
} else {
join.using = [args]
}
}
},
{
name: 'delete',
getter: true,
updateContext: ctx => {
ctx.type = 'delete'
}
},
{
name: 'insert',
updateContext: (ctx, args) => {
Expand Down Expand Up @@ -131,7 +234,7 @@ const methods = [
ctx.frm.push({ type: 'from', args })
ctx.express = 'where'
} else if (ctx.express === 'where') {
ctx.whr.push(args)
ctx.whr.push({ type: 'and', args })
ctx.express = 'return'
} else if (ctx.express === 'return') {
ctx.ret.push(args)
Expand Down
7 changes: 3 additions & 4 deletions packages/sqorn-sql/src/util/conditions.js
Expand Up @@ -4,12 +4,11 @@ const {
snakeCase
} = require('./helpers')

const conditions = ctx => {
const conditions = (ctx, whr) => {
let txt = ''
const calls = ctx.whr
for (let i = 0; i < calls.length; ++i) {
for (let i = 0; i < whr.length; ++i) {
if (i !== 0) txt += ' and '
txt += condition(ctx, calls[i])
txt += condition(ctx, whr[i].args)
}
return txt
}
Expand Down
44 changes: 40 additions & 4 deletions packages/sqorn-sql/src/util/from_items.js
Expand Up @@ -3,20 +3,56 @@ const {
buildTaggedTemplate,
snakeCase
} = require('./helpers')
const { conditions } = require('./conditions')

// utilities for building from_items, see:
// https://www.postgresql.org/docs/9.5/static/sql-select.html

const fromItems = (ctx, calls) => {
const fromItems = (ctx, froms) => {
let txt = ''
for (let i = 0; i < calls.length; ++i) {
for (let i = 0; i < froms.length; ++i) {
const from = froms[i]
if (i !== 0) txt += join(from)
txt += fromItem(ctx, from)
txt += joinConditions(ctx, from)
}
return txt
}

const join = from =>
from.join
? `${from.on || from.using ? '' : ' natural'} ${joins[from.join]} `
: ', '

const joins = {
inner: 'join',
left: 'left join',
right: 'right join',
full: 'full join',
cross: 'cross join'
}

const joinConditions = (ctx, from) =>
(from.on && ` on ${conditions(ctx, from.on)}`) ||
(from.using && ` using (${using(ctx, from.using)})`) ||
''

const using = (ctx, using) => {
let txt = ''
for (let i = 0; i < using.length; ++i) {
const args = using[i]
if (i !== 0) txt += ', '
txt += build(ctx, calls[i].args)
if (typeof args[0] === 'string') {
txt += args.join(', ')
} else {
txt += buildTaggedTemplate(ctx, args)
}
}
return txt
}

const build = (ctx, args) => {
const fromItem = (ctx, from) => {
const { args } = from
if (args === undefined) {
// no from clause
return ''
Expand Down

0 comments on commit e750245

Please sign in to comment.