Comparing version
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
15.58%380
11.44%398
15.7%5
25%+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added