mql2
Advanced tools
Comparing version 0.0.21 to 0.1.0
676
mql.js
import { | ||
is_string, is_function, flatten, flatten as cat, reduce, tap, go, | ||
is_string, is_function, flatten, flatten as cat, reduce, tap, go, pipe, | ||
map, filter, reject, pluck, uniq, each, index_by, group_by, last, object, curry | ||
} from 'fxjs2'; | ||
import { Pool } from 'pg'; | ||
import pg from 'pg'; | ||
import mysql from 'mysql'; | ||
import load_ljoin from './ljoin.js' | ||
import { dump } from 'dumper.js'; | ||
const MQL_DEBUG = { | ||
export const MQL_DEBUG = { | ||
DUMP: false, | ||
@@ -14,4 +15,2 @@ LOG: false | ||
export { MQL_DEBUG }; | ||
const SymbolColumn = Symbol('COLUMN'); | ||
@@ -31,22 +30,2 @@ const SymbolTag = Symbol('TAG'); | ||
const add_column = me => | ||
me.column == '*' ? | ||
COLUMN(me.as + '.*') : | ||
is_column(me.column) ? | ||
COLUMN(...go( | ||
me.column.originals.concat(pluck('left_key', me.rels)), | ||
map(c => me.as + '.' + c), | ||
uniq)) : | ||
tag(SymbolColumn); | ||
const to_qq = () => '??'; | ||
const escape_dq = value => ('' + value).replace(/\\/g, "\\\\").replace(/"/g, '""'); | ||
const dq = str => ('' + str).split('.').map(s => s == '*' ? s : `"${escape_dq(s)}"`).join("."); | ||
const columnize = v => | ||
v == '*' ? | ||
'*' : | ||
v.match(/\s*\sas\s\s*/i) ? | ||
v.split(/\s*\sas\s\s*/i).map(dq).join(' AS ') : | ||
dq(v); | ||
const is_column = f => f && f[SymbolColumn]; | ||
@@ -56,338 +35,401 @@ const is_tag = f => f && f[SymbolTag]; | ||
function ready_sqls(strs, tails) { | ||
const options = strs | ||
.map(s => s | ||
.replace(/\s*\n/, '') | ||
.split('\n') | ||
.map((s) => { | ||
var depth = s.match(/^\s*/)[0].length, | ||
as = s.trim(), | ||
rel_type; | ||
const tag = f => typeof f == 'function' ? | ||
Object.assign(f, { [SymbolTag]: true }) : tag(_ => f); | ||
var prefix = as.substr(0, 2); | ||
if (['- ', '< ', 'x '].includes(prefix)) { | ||
rel_type = prefix.trim(); | ||
as = as.substr(1).trim(); | ||
return { depth, as, rel_type } | ||
} else if (prefix == 'p ') { | ||
rel_type = as[2]; | ||
as = as.substr(3).trim(); | ||
return { depth, as, rel_type, is_poly: true } | ||
} else { | ||
return { depth, as }; | ||
} | ||
}) | ||
); | ||
function BASE({ | ||
create_pool, | ||
query_fn, | ||
get_connection = pool => pool.connect(), | ||
BEGIN = client => client.query('BEGIN'), | ||
COMMIT = async client => { | ||
await client.query('COMMIT'); | ||
return await client.release(); | ||
}, | ||
ROLLBACK = async client => { | ||
await client.query('ROLLBACK'); | ||
return await client.release(); | ||
}, | ||
reg_q = /\?\?/g, | ||
to_q = () => '??', | ||
escape_dq = idtf => `"${('' + idtf).replace(/\\/g, '\\\\').replace(/"/g, '""')}"`, | ||
replace_q = (query) => { | ||
if (is_injection(query)) return SymbolInjection; | ||
let i = 0; | ||
query.text = query.text.replace(reg_q, _ => `$${++i}`); | ||
return query; | ||
}, | ||
use_ljoin | ||
}) { | ||
const add_column = me => | ||
me.column == '*' ? | ||
COLUMN(me.as + '.*') : | ||
is_column(me.column) ? | ||
COLUMN(...go( | ||
me.column.originals.concat(pluck('left_key', me.rels)), | ||
map(c => me.as + '.' + c), | ||
uniq)) : | ||
tag(SymbolColumn); | ||
go( | ||
tails, | ||
map(tail => | ||
is_tag(tail) ? | ||
{ query: tail } : | ||
Object.assign({}, tail, { query: tail.query || tag() }) | ||
), | ||
Object.entries, | ||
each(([i, t]) => go( | ||
options[i], | ||
last, | ||
_ => Object.assign(_, t) | ||
)) | ||
); | ||
const columnize = v => | ||
v == '*' ? | ||
'*' : | ||
v.match(/\s*\sas\s\s*/i) ? | ||
v.split(/\s*\sas\s\s*/i).map(dq).join(' AS ') : | ||
dq(v); | ||
return options; | ||
} | ||
const dq = str => ('' + str).split('.').map(s => s == '*' ? s : escape_dq(s)).join('.'); | ||
function replace_qq(query) { | ||
if (is_injection(query)) return SymbolInjection; | ||
function ready_sqls(strs, tails) { | ||
const options = strs | ||
.map(s => s | ||
.replace(/\s*\n/, '') | ||
.split('\n') | ||
.map((s) => { | ||
var depth = s.match(/^\s*/)[0].length, | ||
as = s.trim(), | ||
rel_type; | ||
let i = 0; | ||
query.text = query.text.replace(/\?\?/g, _ => `$${++i}`); | ||
return query; | ||
} | ||
var prefix = as.substr(0, 2); | ||
if (['- ', '< ', 'x '].includes(prefix)) { | ||
rel_type = prefix.trim(); | ||
as = as.substr(1).trim(); | ||
return { depth, as, rel_type } | ||
} else if (prefix == 'p ') { | ||
rel_type = as[2]; | ||
as = as.substr(3).trim(); | ||
return { depth, as, rel_type, is_poly: true } | ||
} else { | ||
return { depth, as }; | ||
} | ||
}) | ||
); | ||
function merge_query(queries) { | ||
if (queries.find(is_injection)) return SymbolInjection; | ||
go( | ||
tails, | ||
map(tail => | ||
is_tag(tail) ? | ||
{ query: tail } : | ||
Object.assign({}, tail, { query: tail.query || tag() }) | ||
), | ||
Object.entries, | ||
each(([i, t]) => go( | ||
options[i], | ||
last, | ||
_ => Object.assign(_, t) | ||
)) | ||
); | ||
var query = reduce((res, query) => { | ||
if (!query) return res; | ||
if (query.text) res.text += (' ' + query.text); | ||
if (query.values) res.values.push(...query.values); | ||
return res; | ||
}, queries, { | ||
text: '', | ||
values: [] | ||
}); | ||
query.text = query.text.replace(/\n/g, ' ').replace(/\s\s*/g, ' ').trim(); | ||
return query; | ||
} | ||
return options; | ||
} | ||
export function VALUES(values) { | ||
return tag(function () { | ||
values = Array.isArray(values) ? values : [values]; | ||
function merge_query(queries) { | ||
if (queries.find(is_injection)) return SymbolInjection; | ||
const columns = go( | ||
values, | ||
map(Object.keys), | ||
flatten, | ||
uniq); | ||
var query = reduce((res, query) => { | ||
if (!query) return res; | ||
if (query.text) res.text += (' ' + query.text); | ||
if (query.values) res.values.push(...query.values); | ||
return res; | ||
}, queries, { | ||
text: '', | ||
values: [] | ||
}); | ||
query.text = query.text.replace(/\n/g, ' ').replace(/\s\s*/g, ' ').trim(); | ||
return query; | ||
} | ||
const DEFAULTS = go( | ||
columns, | ||
map(k => [k, SymbolDefault]), | ||
object); | ||
function VALUES(values) { | ||
return tag(function () { | ||
values = Array.isArray(values) ? values : [values]; | ||
values = values | ||
.map(v => Object.assign({}, DEFAULTS, v)) | ||
.map(v => Object.values(v)); | ||
const columns = go( | ||
values, | ||
map(Object.keys), | ||
flatten, | ||
uniq); | ||
return { | ||
text: `(${COLUMN(...columns)().text}) VALUES (${ | ||
values | ||
.map(v => v.map(v => v == SymbolDefault ? 'DEFAULT' : to_qq()).join(', ')) | ||
.join('), (')})`, | ||
values: flatten(values.map(v => v.filter(v => v != SymbolDefault))) | ||
} | ||
}); | ||
} | ||
const DEFAULTS = go( | ||
columns, | ||
map(k => [k, SymbolDefault]), | ||
object); | ||
const tag = f => typeof f == 'function' ? | ||
Object.assign(f, { [SymbolTag]: true }) : tag(_ => f); | ||
values = values | ||
.map(v => Object.assign({}, DEFAULTS, v)) | ||
.map(v => Object.values(v)); | ||
export function COLUMN(...originals) { | ||
return Object.assign(tag(function() { | ||
return { | ||
text: originals | ||
.map(v => | ||
is_string(v) ? | ||
columnize(v) : | ||
Object | ||
.entries(v) | ||
.map(v => v.map(dq).join(' AS ')) | ||
.join(', ')) | ||
.join(', ') | ||
}; | ||
}), { [SymbolColumn]: true, originals: originals }); | ||
} | ||
return { | ||
text: `(${COLUMN(...columns)().text}) VALUES (${ | ||
values | ||
.map(v => v.map(v => v == SymbolDefault ? 'DEFAULT' : to_q()).join(', ')) | ||
.join('), (')})`, | ||
values: flatten(values.map(v => v.filter(v => v != SymbolDefault))) | ||
} | ||
}); | ||
} | ||
export const CL = COLUMN, TABLE = COLUMN, TB = TABLE; | ||
function COLUMN(...originals) { | ||
return Object.assign(tag(function() { | ||
return { | ||
text: originals | ||
.map(v => | ||
is_string(v) ? | ||
columnize(v) : | ||
Object | ||
.entries(v) | ||
.map(v => v.map(dq).join(' AS ')) | ||
.join(', ')) | ||
.join(', ') | ||
}; | ||
}), { [SymbolColumn]: true, originals: originals }); | ||
} | ||
function PARAMS(obj, sep) { | ||
return tag(function() { | ||
let i = 0; | ||
const text = Object.keys(obj).map(k => `${columnize(k)} = ${to_qq()}`).join(sep); | ||
const values = Object.values(obj); | ||
const CL = COLUMN, TABLE = COLUMN, TB = TABLE; | ||
function PARAMS(obj, sep) { | ||
return tag(function() { | ||
let i = 0; | ||
const text = Object.keys(obj).map(k => `${columnize(k)} = ${to_q()}`).join(sep); | ||
const values = Object.values(obj); | ||
return { | ||
text: text.replace(reg_q, function() { | ||
const value = values[i++]; | ||
return is_column(value) ? value().text : to_q() | ||
}), | ||
values: reject(is_column, values) | ||
}; | ||
}); | ||
} | ||
function EQ(obj, sep = 'AND') { | ||
return PARAMS(obj, ' ' + sep + ' '); | ||
} | ||
function SET(obj) { | ||
return tag(function() { | ||
const query = PARAMS(obj, ', ')(); | ||
query.text = 'SET ' + query.text; | ||
return query; | ||
}); | ||
} | ||
function BASE_IN(key, operator, values) { | ||
values = uniq(values); | ||
var keys_text = COLUMN(...wrap_arr(key))().text; | ||
return { | ||
text: text.replace(/\?\?/g, function() { | ||
const value = values[i++]; | ||
return is_column(value) ? value().text : to_qq() | ||
}), | ||
values: reject(is_column, values) | ||
text: `${Array.isArray(key) ? `(${keys_text})` : keys_text} ${operator} (${values.map( | ||
Array.isArray(key) ? v => `(${v.map(to_q).join(', ')})` : to_q | ||
).join(', ')})`, | ||
values: cat(values) | ||
}; | ||
}); | ||
} | ||
} | ||
export function EQ(obj, sep = 'AND') { | ||
return PARAMS(obj, ' ' + sep + ' '); | ||
} | ||
function IN(key, values) { | ||
return tag(function() { | ||
return BASE_IN(key, 'IN', values); | ||
}); | ||
} | ||
export function SET(obj) { | ||
return tag(function() { | ||
const query = PARAMS(obj, ', ')(); | ||
query.text = 'SET ' + query.text; | ||
return query; | ||
}); | ||
} | ||
function NOT_IN(key, values) { | ||
return tag(function() { | ||
return BASE_IN(key, 'NOT IN', values); | ||
}); | ||
} | ||
function BASE_IN(key, operator, values) { | ||
values = uniq(values); | ||
function _SQL(texts, values) { | ||
return go( | ||
mix( | ||
texts.map(text => ({ text })), | ||
values.map(value => | ||
is_tag(value) ? value() : is_function(value) ? SymbolInjection : { text: to_q(), values: [value] }) | ||
), | ||
merge_query); | ||
} | ||
var keys_text = COLUMN(...wrap_arr(key))().text; | ||
return { | ||
text: `${Array.isArray(key) ? `(${keys_text})` : keys_text} ${operator} (${values.map( | ||
Array.isArray(key) ? v => `(${v.map(to_qq).join(', ')})` : to_qq | ||
).join(', ')})`, | ||
values: cat(values) | ||
}; | ||
} | ||
function SQL(texts, ...values) { | ||
return tag(function() { | ||
return _SQL(texts, values); | ||
}); | ||
} | ||
export function IN(key, values) { | ||
return tag(function() { | ||
return BASE_IN(key, 'IN', values); | ||
}); | ||
} | ||
function SQLS(sqls) { | ||
return tag(function() { | ||
return sqls.find(sql => !is_tag(sql)) ? | ||
SymbolInjection : merge_query(sqls.map(sql => sql())); | ||
}); | ||
} | ||
export function NOT_IN(key, values) { | ||
return tag(function() { | ||
return BASE_IN(key, 'NOT IN', values); | ||
}); | ||
} | ||
function baseAssociate(QUERY) { | ||
return async function(strs, ...tails) { | ||
return go( | ||
ready_sqls(strs, tails), | ||
cat, | ||
filter(t => t.as), | ||
each(option => { | ||
option.column = option.column || '*'; | ||
option.query = option.query || tag(); | ||
option.table = option.table || (option.rel_type == '-' ? option.as + 's' : option.as); | ||
option.rels = []; | ||
}), | ||
function setting([left, ...rest]) { | ||
const cur = [left]; | ||
each(me => { | ||
while (!(last(cur).depth < me.depth)) cur.pop(); | ||
const left = last(cur); | ||
left.rels.push(me); | ||
if (me.rel_type == '-') { | ||
me.left_key = me.left_key || (me.is_poly ? 'id' : me.table.substr(0, me.table.length-1) + '_id'); | ||
me.where_key = me.key || (me.is_poly ? 'attached_id' : 'id'); | ||
me.xjoin = tag(); | ||
} else if (me.rel_type == '<') { | ||
me.left_key = me.left_key || 'id'; | ||
me.where_key = me.key || (me.is_poly ? 'attached_id' : left.table.substr(0, left.table.length-1) + '_id'); | ||
me.xjoin = tag(); | ||
} else if (me.rel_type == 'x') { | ||
me.left_key = me.left_key || 'id'; | ||
me.where_key = '_#_xtable_#_.' + (me.left_xkey || left.table.substr(0, left.table.length-1) + '_id'); | ||
var xtable = me.xtable || (left.table + '_' + me.table); | ||
me.xjoin = SQL `INNER JOIN ${TB(xtable)} as ${escape_dq('_#_xtable_#_')} on ${EQ({ | ||
['_#_xtable_#_.' + (me.xkey || me.table.substr(0, me.table.length-1) + '_id')]: COLUMN(me.as + '.' + (me.key || 'id')) | ||
})}`; | ||
} | ||
me.poly_type = me.is_poly ? | ||
SQL `AND ${EQ( | ||
(me.poly_type && typeof me.poly_type == 'object') ? me.poly_type : { attached_type: me.poly_type || left.table } | ||
)}` : tag(); | ||
cur.push(me); | ||
}, rest); | ||
return left; | ||
}, | ||
async function(me) { | ||
const lefts = await QUERY ` | ||
SELECT ${add_column(me)} | ||
FROM ${TB(me.table)} AS ${TB(me.as)} ${me.query}`; | ||
function _SQL(texts, values) { | ||
return go( | ||
mix( | ||
texts.map(text => ({ text })), | ||
values.map(value => | ||
is_tag(value) ? value() : is_function(value) ? SymbolInjection : { text: to_qq(), values: [value] }) | ||
), | ||
merge_query); | ||
} | ||
return go( | ||
[lefts, me], | ||
function recur([lefts, option]) { | ||
return lefts.length && option.rels.length && go(option.rels, cmap(async function(me) { | ||
const query = me.query(); | ||
if (query && query.text) query.text = 'AND ' + query.text.replace(/WHERE|AND/i, ''); | ||
export function SQL(texts, ...values) { | ||
return tag(function() { | ||
return _SQL(texts, values); | ||
}); | ||
} | ||
var fold_key = me.rel_type == 'x' ? | ||
`_#_${me.where_key.split('.')[1]}_#_` : me.where_key; | ||
export function SQLS(sqls) { | ||
return tag(function() { | ||
return sqls.find(sql => !is_tag(sql)) ? | ||
SymbolInjection : merge_query(sqls.map(sql => sql())); | ||
}); | ||
} | ||
var colums = uniq(add_column(me).originals.concat(me.where_key + (me.rel_type == 'x' ? ` AS ${fold_key}` : ''))); | ||
function baseAssociate(QUERY) { | ||
return async function(strs, ...tails) { | ||
return go( | ||
ready_sqls(strs, tails), | ||
cat, | ||
filter(t => t.as), | ||
each(option => { | ||
option.column = option.column || '*'; | ||
option.query = option.query || tag(); | ||
option.table = option.table || (option.rel_type == '-' ? option.as + 's' : option.as); | ||
option.rels = []; | ||
}), | ||
function setting([left, ...rest]) { | ||
const cur = [left]; | ||
each(me => { | ||
while (!(last(cur).depth < me.depth)) cur.pop(); | ||
const left = last(cur); | ||
left.rels.push(me); | ||
if (me.rel_type == '-') { | ||
me.left_key = me.left_key || (me.is_poly ? 'id' : me.table.substr(0, me.table.length-1) + '_id'); | ||
me.where_key = me.key || (me.is_poly ? 'attached_id' : 'id'); | ||
me.xjoin = tag(); | ||
} else if (me.rel_type == '<') { | ||
me.left_key = me.left_key || 'id'; | ||
me.where_key = me.key || (me.is_poly ? 'attached_id' : left.table.substr(0, left.table.length-1) + '_id'); | ||
me.xjoin = tag(); | ||
} else if (me.rel_type == 'x') { | ||
me.left_key = me.left_key || 'id'; | ||
me.where_key = '_#_xtable_#_.' + (me.left_xkey || left.table.substr(0, left.table.length-1) + '_id'); | ||
var xtable = me.xtable || (left.table + '_' + me.table); | ||
me.xjoin = SQL `INNER JOIN ${TB(xtable)} as "_#_xtable_#_" on ${EQ({ | ||
['_#_xtable_#_.' + (me.xkey || me.table.substr(0, me.table.length-1) + '_id')]: COLUMN(me.as + '.' + (me.key || 'id')) | ||
})}`; | ||
} | ||
me.poly_type = me.is_poly ? | ||
SQL `AND ${EQ( | ||
(me.poly_type && typeof me.poly_type == 'object') ? me.poly_type : { attached_type: me.poly_type || left.table } | ||
)}` : tag(); | ||
cur.push(me); | ||
}, rest); | ||
return left; | ||
}, | ||
async function(me) { | ||
const lefts = await QUERY ` | ||
SELECT ${add_column(me)} | ||
FROM ${TB(me.table)} AS ${TB(me.as)} ${me.query}`; | ||
var in_vals = filter(a => a != null, pluck(me.left_key, lefts)); | ||
return go( | ||
[lefts, me], | ||
function recur([lefts, option]) { | ||
return lefts.length && option.rels.length && go(option.rels, cmap(async function(me) { | ||
const query = me.query(); | ||
if (query && query.text) query.text = 'AND ' + query.text.replace(/WHERE|AND/i, ''); | ||
const rights = !in_vals.length ? [] : await QUERY ` | ||
SELECT ${COLUMN(...colums)} | ||
FROM ${TB(me.table)} AS ${TB(me.as)} | ||
${me.xjoin} | ||
WHERE | ||
${IN(me.where_key, in_vals)} | ||
${me.poly_type} | ||
${tag(query)}`; | ||
var fold_key = me.rel_type == 'x' ? | ||
`_#_${me.where_key.split('.')[1]}_#_` : me.where_key; | ||
var [folder, default_value] = me.rel_type == '-' ? [index_by, () => ({})] : [group_by, () => []]; | ||
var colums = uniq(add_column(me).originals.concat(me.where_key + (me.rel_type == 'x' ? ` AS ${fold_key}` : ''))); | ||
return go( | ||
rights, | ||
folder(a => a[fold_key]), | ||
folded => each(function(left) { | ||
left._ = left._ || {}; | ||
left._[me.as] = folded[left[me.left_key]] || default_value(); | ||
}, lefts), | ||
() => recur([rights, me])); | ||
})); | ||
}, | ||
_ => lefts | ||
); | ||
} | ||
); | ||
} | ||
} | ||
var in_vals = filter(a => a != null, pluck(me.left_key, lefts)); | ||
async function CONNECT(connection_info) { | ||
const pool = create_pool(connection_info); | ||
const pool_query = query_fn(pool); | ||
const rights = !in_vals.length ? [] : await QUERY ` | ||
SELECT ${COLUMN(...colums)} | ||
FROM ${TB(me.table)} AS ${TB(me.as)} | ||
${me.xjoin} | ||
WHERE | ||
${IN(me.where_key, in_vals)} | ||
${me.poly_type} | ||
${tag(query)}`; | ||
function base_query(excute_query, texts, values) { | ||
return go( | ||
_SQL(texts, values), | ||
replace_q, | ||
query => is_injection(query) ? Promise.reject('INJECTION ERROR') : query, | ||
tap(function(query) { | ||
if (MQL_DEBUG.DUMP) dump(query); | ||
typeof MQL_DEBUG.LOG == 'function' ? | ||
MQL_DEBUG.LOG(query) : MQL_DEBUG.LOG && console.log(query); | ||
}), | ||
excute_query); | ||
} | ||
var [folder, default_value] = me.rel_type == '-' ? [index_by, () => ({})] : [group_by, () => []]; | ||
async function QUERY(texts, ...values) { | ||
return base_query(pool_query, texts, values); | ||
} | ||
return go( | ||
rights, | ||
folder(a => a[fold_key]), | ||
folded => each(function(left) { | ||
left._ = left._ || {}; | ||
left._[me.as] = folded[left[me.left_key]] || default_value(); | ||
}, lefts), | ||
() => recur([rights, me])); | ||
})); | ||
}, | ||
_ => lefts | ||
); | ||
const ljoin = use_ljoin ? await load_ljoin({ | ||
ready_sqls, add_column, tag, MQL_DEBUG, | ||
connection_info, QUERY, VALUES, IN, NOT_IN, EQ, SET, COLUMN, CL, TABLE, TB, SQL, SQLS | ||
}) : _ => _; | ||
return { | ||
VALUES, IN, NOT_IN, EQ, SET, COLUMN, CL, TABLE, TB, SQL, MQL_DEBUG, | ||
QUERY, | ||
ASSOCIATE: baseAssociate(QUERY), | ||
LJOIN: ljoin(QUERY), | ||
async TRANSACTION() { | ||
try { | ||
const client = await get_connection(pool); | ||
const client_query = query_fn(client); | ||
await BEGIN(client); | ||
function QUERY(texts, ...values) { | ||
return base_query(client_query, texts, values); | ||
} | ||
return { | ||
VALUES, IN, NOT_IN, EQ, SET, COLUMN, CL, TABLE, TB, SQL, | ||
QUERY, | ||
ASSOCIATE: baseAssociate(QUERY), | ||
LJOIN: ljoin(QUERY), | ||
COMMIT: _ => COMMIT(client), | ||
ROLLBACK: _ => ROLLBACK(client) | ||
} | ||
} catch (e) { throw e; } | ||
} | ||
); | ||
} | ||
} | ||
return { CONNECT, VALUES, IN, NOT_IN, EQ, SET, COLUMN, CL, TABLE, TB, SQL, MQL_DEBUG } | ||
} | ||
export async function CONNECT(connection) { | ||
const pool = new Pool(connection); | ||
const pool_query = pool.query.bind(pool); | ||
const method_promise = curry((name, obj) => | ||
new Promise((resolve, reject) => | ||
obj[name]((err, res) => err ? reject(err) : resolve(res)))); | ||
function base_query(excute_query, texts, values) { | ||
return go( | ||
_SQL(texts, values), | ||
replace_qq, | ||
query => is_injection(query) ? Promise.reject('INJECTION ERROR') : query, | ||
tap(function(query) { | ||
if (MQL_DEBUG.DUMP) dump(query); | ||
typeof MQL_DEBUG.LOG == 'function' ? | ||
MQL_DEBUG.LOG(query) : MQL_DEBUG.LOG && console.log(query); | ||
}), | ||
excute_query, | ||
res => res.rows); | ||
} | ||
export const | ||
PostgreSQL = BASE({ | ||
create_pool: connection_info => new pg.Pool(connection_info), | ||
async function QUERY(texts, ...values) { | ||
return base_query(pool_query, texts, values); | ||
} | ||
query_fn: pool => pipe(pool.query.bind(pool), res => res.rows), | ||
const ljoin = await load_ljoin({ | ||
ready_sqls, add_column, tag, MQL_DEBUG, | ||
connection, QUERY, VALUES, IN, NOT_IN, EQ, SET, COLUMN, CL, TABLE, TB, SQL, SQLS | ||
}); | ||
use_ljoin: true | ||
}), | ||
MySQL = BASE({ | ||
create_pool: connection_info => mysql.createPool(connection_info), | ||
return { | ||
VALUES, IN, NOT_IN, EQ, SET, COLUMN, CL, TABLE, TB, SQL, | ||
query_fn: pool => ({text, values}) => | ||
new Promise((resolve, reject) => | ||
pool.query(text, values, (err, results) => | ||
err ? reject(err) : resolve(results))), | ||
QUERY, | ||
ASSOCIATE: baseAssociate(QUERY), | ||
LJOIN: ljoin(QUERY), | ||
get_connection: method_promise('getConnection'), | ||
BEGIN: method_promise('beginTransaction'), | ||
COMMIT: method_promise('commit'), | ||
ROLLBACK: method_promise('rollback'), | ||
async TRANSACTION() { | ||
const client = await pool.connect(); | ||
const client_query = client.query.bind(client); | ||
await client.query('BEGIN'); | ||
function QUERY(texts, ...values) { | ||
return base_query(client_query, texts, values); | ||
} | ||
return { | ||
QUERY, | ||
ASSOCIATE: baseAssociate(QUERY), | ||
LJOIN: ljoin(QUERY), | ||
async COMMIT() { | ||
await client.query('COMMIT'); | ||
return await client.release(); | ||
}, | ||
async ROLLBACK() { | ||
await client.query('ROLLBACK'); | ||
return await client.release(); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
reg_q: /\?/g, | ||
to_q: () => '?', | ||
escape_dq: mysql.escapeId, | ||
replace_q: _ => _ | ||
}); |
{ | ||
"name": "mql2", | ||
"version": "0.0.21", | ||
"version": "0.1.0", | ||
"description": "query builder", | ||
@@ -29,2 +29,3 @@ "main": "index.js", | ||
"fxjs2": "0.0.4", | ||
"mysql": "^2.16.0", | ||
"pg": "^7.4.3" | ||
@@ -31,0 +32,0 @@ }, |
@@ -1,2 +0,2 @@ | ||
# MQL - Node.js PostgreSQL 쿼리 빌더 | ||
# MQL - Node.js 데이터베이스 쿼리 빌더 | ||
@@ -12,3 +12,25 @@ ## 특징 | ||
- 인자와 결과 값을 자바스크립트의 기본 값으로만(object, array, string, number, true, false, null) 구성하여, 조합성이 높고 JSON 변환 비용이 없습니다. | ||
- PostgreSQL, MySQL 지원 | ||
## 목차 | ||
- [설치](#설치) | ||
- [연결](#연결) | ||
- [PostgreSQL](#postgresql) | ||
- [MySQL](#mysql) | ||
- [간단한 쿼리](#간단한-쿼리) | ||
- [함수 불러오기](#함수-불러오기) | ||
- [지원하는 헬퍼 함수](#지원하는-헬퍼-함수) | ||
- [EQ](#eq) | ||
- [IN](#in) | ||
- [NOT_IN](#not_in) | ||
- [VALUES](#values) | ||
- [SET](#set) | ||
- [Associations](#associations) | ||
- [기본](#기본) | ||
- [Polymorphic](#polymorphic) | ||
- [Transaction](#transaction) | ||
- [Many to many](#many-to-many) | ||
- [옵션](#옵션) | ||
- [DEBUG](#debug) | ||
## 설치 | ||
@@ -22,4 +44,7 @@ | ||
### PostgreSQL | ||
```javascript | ||
const { CONNECT } = require('mql2'); | ||
const { PostgreSQL } = require('mql2'); | ||
const { CONNECT } = PostgreSQL; | ||
const POOL = await CONNECT({ | ||
@@ -34,2 +59,23 @@ host: 'localhost', | ||
### PostgreSQL Connection 옵션 | ||
MQL은 내부적으로 node-postgres를 사용합니다. `CONNECT` 함수에 사용되는 옵션은 node-postgres와 동일합니다. [디비 연결](https://node-postgres.com/features/connecting)이나 [커넥션 풀](https://node-postgres.com/api/pool)과 관련된 자세한 옵션은 [node-postgres](https://node-postgres.com/) 사이트에서 확인할 수 있습니다. | ||
### MySQL | ||
```javascript | ||
const { MySQL } = require('mql2'); | ||
const { CONNECT } = MySQL; | ||
const POOL = await CONNECT({ | ||
host: 'localhost', | ||
user: 'username', | ||
password: '1234', | ||
database: 'dbname' | ||
}); | ||
``` | ||
### MySQL Connection 옵션 | ||
MQL은 내부적으로 mysql를 사용합니다. `CONNECT` 함수에 사용되는 옵션은 mysql과 동일합니다. [디비 연결](https://github.com/mysqljs/mysql#connection-options)이나 [커넥션 풀](https://github.com/mysqljs/mysql#pool-options)과 관련된 자세한 옵션은 [mysql](https://github.com/mysqljs/mysql) 사이트에서 확인할 수 있습니다. | ||
## 간단한 쿼리 | ||
@@ -44,6 +90,19 @@ | ||
`CONNECT`를 통해 얻은 객체를 통해 얻은 `QUERY`는 connection pool을 이용합니다. | ||
`CONNECT`를 통해 얻은 `QUERY`는 connection pool을 이용합니다. | ||
## 지원하는 tags | ||
## 함수 불러오기 | ||
```javascript | ||
const POOL = await CONNECT(); | ||
const = { | ||
VALUES, IN, NOT_IN, EQ, SET, COLUMN, CL, TABLE, TB, SQL, MQL_DEBUG, | ||
QUERY, | ||
ASSOCIATE, | ||
LJOIN, | ||
TRANSACTION | ||
} = POOL; | ||
``` | ||
## 지원하는 헬퍼 함수 | ||
### EQ | ||
@@ -309,3 +368,4 @@ | ||
```javascript | ||
const { CONNECT } = require('mql2'); | ||
const { PostgreSQL } = require('mql2'); | ||
const { CONNECT } = PostgreSQL; | ||
const POOL = await CONNECT({ | ||
@@ -330,3 +390,3 @@ host: 'localhost', | ||
`TRANSACTION`을 통해 얻은 객체를 통해 얻은 `QUERY`는 하나의 connection을 이용합니다. `ROLLBACK`이나 `COMMIT`후에는 위에서 만든 QUERY 함수는 더이상 사용할 수 없습니다. | ||
`TRANSACTION`을 통해 얻은 `QUERY`는 하나의 connection을 이용합니다. `ROLLBACK`이나 `COMMIT`을 하고나면 앞서 함께 얻었던 `QUERY` 함수의 커넥션은 해제되고 더이상 사용할 수 없습니다. | ||
@@ -338,4 +398,2 @@ ## DEBUG | ||
```javascript | ||
const { MQL_DEBUG } = require('mql2'); | ||
MQL_DEBUG.LOG = true; | ||
@@ -347,5 +405,1 @@ QUERY `SELECT ${"hi~"} as ho`; | ||
## Connection Pool 옵션 | ||
MQL은 내부적으로 node-postgres를 사용합니다. `CONNECT` 함수에 사용되는 옵션은 node-postgres와 동일합니다. [디비 연결](https://node-postgres.com/features/connecting)이나 [커넥션 풀](https://node-postgres.com/api/pool)과 관련된 자세한 옵션은 [node-postgres](https://node-postgres.com/) 사이트에서 확인할 수 있습니다. | ||
25129
380
398
5
+ Addedmysql@^2.16.0
+ Addedbignumber.js@9.0.0(transitive)
+ Addedcore-util-is@1.0.3(transitive)
+ Addedinherits@2.0.4(transitive)
+ Addedisarray@1.0.0(transitive)
+ Addedmysql@2.18.1(transitive)
+ Addedprocess-nextick-args@2.0.1(transitive)
+ Addedreadable-stream@2.3.7(transitive)
+ Addedsafe-buffer@5.1.2(transitive)
+ Addedsqlstring@2.3.1(transitive)
+ Addedstring_decoder@1.1.1(transitive)
+ Addedutil-deprecate@1.0.2(transitive)