sql-love 1.0.3 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -32
- package/dist/index.cjs +16 -0
- package/dist/index.d.ts +732 -9
- package/dist/index.mjs +16 -0
- package/package.json +27 -10
- package/.github/workflows/node.js.yml +0 -37
- package/.prettierrc.json +0 -13
- package/CHANGELOG.md +0 -39
- package/CONTRIBUTING.md +0 -85
- package/assets/sql-love-logo.png +0 -0
- package/codecov.yml +0 -7
- package/dist/index.js +0 -9
- package/dist/src/Parser/Parser.d.ts +0 -157
- package/dist/src/Parser/Parser.js +0 -304
- package/dist/src/SelectBuilder/SelectBuilder.d.ts +0 -379
- package/dist/src/SelectBuilder/SelectBuilder.js +0 -810
- package/dist/src/extractGrouped/extractGrouped.d.ts +0 -17
- package/dist/src/extractGrouped/extractGrouped.js +0 -31
- package/dist/src/extractIndexed/extractIndexed.d.ts +0 -12
- package/dist/src/extractIndexed/extractIndexed.js +0 -23
- package/dist/src/extractLookup/extractLookup.d.ts +0 -9
- package/dist/src/extractLookup/extractLookup.js +0 -21
- package/dist/src/getPagination/getPagination.d.ts +0 -16
- package/dist/src/getPagination/getPagination.js +0 -40
- package/dist/src/runMysql/runMysql.d.ts +0 -58
- package/dist/src/runMysql/runMysql.js +0 -61
- package/dist/src/runPrisma/runPrisma.d.ts +0 -41
- package/dist/src/runPrisma/runPrisma.js +0 -34
- package/dist/src/substrCount/substrCount.d.ts +0 -6
- package/dist/src/substrCount/substrCount.js +0 -14
- package/dist/src/toSafeJson/toSafeJson.d.ts +0 -25
- package/dist/src/toSafeJson/toSafeJson.js +0 -45
- package/index.ts +0 -19
- package/src/Parser/Parser.spec.ts +0 -226
- package/src/Parser/Parser.ts +0 -325
- package/src/SelectBuilder/SelectBuilder.spec.ts +0 -785
- package/src/SelectBuilder/SelectBuilder.ts +0 -914
- package/src/extractGrouped/extractGrouped.spec.ts +0 -43
- package/src/extractGrouped/extractGrouped.ts +0 -36
- package/src/extractIndexed/extractIndexed.spec.ts +0 -40
- package/src/extractIndexed/extractIndexed.ts +0 -28
- package/src/extractLookup/extractLookup.spec.ts +0 -38
- package/src/extractLookup/extractLookup.ts +0 -33
- package/src/getPagination/getPagination.spec.ts +0 -75
- package/src/getPagination/getPagination.ts +0 -42
- package/src/runMysql/runMysql.spec.ts +0 -286
- package/src/runMysql/runMysql.ts +0 -106
- package/src/runPrisma/runPrisma.spec.ts +0 -99
- package/src/runPrisma/runPrisma.ts +0 -75
- package/src/substrCount/substrCount.spec.ts +0 -29
- package/src/substrCount/substrCount.ts +0 -14
- package/src/toSafeJson/toSafeJson.spec.ts +0 -28
- package/src/toSafeJson/toSafeJson.ts +0 -56
- package/tsconfig.build.json +0 -10
- package/tsconfig.json +0 -22
- package/vitest.config.ts +0 -11
package/README.md
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
<img alt="SQL Love Logo" src="https://github.com/kensnyder/sql-love/raw/main/assets/sql-love-logo.png?v=1.0.
|
|
1
|
+
<img alt="SQL Love Logo" src="https://github.com/kensnyder/sql-love/raw/main/assets/sql-love-logo.png?v=1.0.4" width="250" height="208" />
|
|
2
2
|
|
|
3
3
|
# sql-love
|
|
4
4
|
|
|
5
|
-
[](https://npmjs.com/package/sql-love)
|
|
6
|
+
[](https://github.com/search?q=repo:kensnyder/sql-love++language:TypeScript&type=code)
|
|
7
|
+
[](https://github.com/kensnyder/sql-love/actions)
|
|
8
|
+
[](https://codecov.io/gh/kensnyder/sql-love)
|
|
9
|
+
[](https://bundlephobia.com/package/sql-love@1.0.4)
|
|
10
|
+
[](https://www.npmjs.com/package/sql-love?activeTab=dependencies)
|
|
11
|
+
[](https://www.npmjs.com/package/sql-love)
|
|
12
|
+
[](https://opensource.org/licenses/ISC)
|
|
10
13
|
|
|
11
14
|
Classes for parsing and building SQL select queries in Node
|
|
12
15
|
|
|
@@ -65,11 +68,11 @@ if (areaCode) {
|
|
|
65
68
|
query.sort(sortField);
|
|
66
69
|
query.limit(limitTo);
|
|
67
70
|
const { sql, bindings } = query.compile();
|
|
68
|
-
```
|
|
69
71
|
|
|
70
|
-
|
|
72
|
+
//
|
|
73
|
+
// Then execute the SQL in your preferred client:
|
|
74
|
+
//
|
|
71
75
|
|
|
72
|
-
```js
|
|
73
76
|
// mysql2:
|
|
74
77
|
connection.query(sql, bindings, (err, results, fields) => {});
|
|
75
78
|
|
|
@@ -82,11 +85,13 @@ const { results } = await env.DB.prepare(sql)
|
|
|
82
85
|
.all();
|
|
83
86
|
```
|
|
84
87
|
|
|
85
|
-
_Note
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
_Note:_ These are prepared statements so the values in the `bindings` array are
|
|
89
|
+
safe from SQL injection, with the caveat that you are in charge of quoting any
|
|
90
|
+
identifiers. For instance, don't populate `u.email` from user input in the
|
|
91
|
+
example above. But if you need dynamic identifiers for some reason, be sure to
|
|
92
|
+
use your SQL client's `quoteIdentifier()` function.
|
|
93
|
+
|
|
94
|
+
### Parsing with placeholders
|
|
90
95
|
|
|
91
96
|
It is possible to add placeholders to the base query.
|
|
92
97
|
|
|
@@ -120,7 +125,12 @@ And "bindings" equals:
|
|
|
120
125
|
*/
|
|
121
126
|
```
|
|
122
127
|
|
|
123
|
-
|
|
128
|
+
### RDBMS engine
|
|
129
|
+
|
|
130
|
+
You may specify a RMDBS engine to compile for. The default is `"mysql"`.
|
|
131
|
+
Supported values are `"mysql" | "sqlite" | "pg" | "mssql" | "oracle"`. Each
|
|
132
|
+
RMDBS engine needs slightly different SQL depending on the syntax for binding
|
|
133
|
+
placeholders and for limit/offset.
|
|
124
134
|
|
|
125
135
|
```js
|
|
126
136
|
import { SelectBuilder, setDefaultEngine } from 'sql-love';
|
|
@@ -150,13 +160,13 @@ setDefaultEngine('pg');
|
|
|
150
160
|
You should use prepared statements to run the sql and bindings returned by
|
|
151
161
|
`SelectBuilder.compile()`. This will protect you from SQL injection.
|
|
152
162
|
|
|
153
|
-
Using `
|
|
163
|
+
Using mysql2's `client.query(sql, bindings)` or Prisma's
|
|
154
164
|
`prisma.$queryRawUnsafe(sql, ...bindings)` are examples of using prepared
|
|
155
165
|
statements.
|
|
156
166
|
|
|
157
|
-
Note that even though the name `prisma.$queryRawUnsafe
|
|
158
|
-
|
|
159
|
-
`prisma.$queryRawUnsafe`
|
|
167
|
+
Note that even though the name `prisma.$queryRawUnsafe` implies that the
|
|
168
|
+
operation is unsafe, values are bound to a prepared statement and you would be
|
|
169
|
+
using `prisma.$queryRawUnsafe` as intended.
|
|
160
170
|
|
|
161
171
|
SQL Love also includes utility functions for running queries in mysql and
|
|
162
172
|
Prisma so you don't have to remember these functions. See documentation for
|
|
@@ -166,7 +176,7 @@ Prisma so you don't have to remember these functions. See documentation for
|
|
|
166
176
|
### Building the query
|
|
167
177
|
|
|
168
178
|
Use the following methods to build queries. And see
|
|
169
|
-
[more examples below](#
|
|
179
|
+
[more examples below](#more-examples).
|
|
170
180
|
|
|
171
181
|
- `query.where(column, operator, value)` - Require column satisfy operator
|
|
172
182
|
- `query.where(column, value)` - Require column equal a value
|
|
@@ -315,7 +325,7 @@ const { sql } = query.compileCount({ countExpr: 'DISTINCT externalId' });
|
|
|
315
325
|
// SELECT COUNT(DISTINCT externalId) AS found_rows FROM users
|
|
316
326
|
```
|
|
317
327
|
|
|
318
|
-
With queries that have a
|
|
328
|
+
With queries that have a `HAVING` clause, the main query will be wrapped in a
|
|
319
329
|
count query.
|
|
320
330
|
|
|
321
331
|
```js
|
|
@@ -326,6 +336,10 @@ const query = new SelectBuilder(`
|
|
|
326
336
|
HAVING COUNT(*) > 1
|
|
327
337
|
`);
|
|
328
338
|
|
|
339
|
+
// Regular query
|
|
340
|
+
const { sql } = query.compile();
|
|
341
|
+
|
|
342
|
+
// Count query
|
|
329
343
|
const { sql } = query.compileCount();
|
|
330
344
|
/*
|
|
331
345
|
SELECT COUNT(*) AS found_rows FROM (
|
|
@@ -343,8 +357,10 @@ SelectBuilder has a few other useful methods.
|
|
|
343
357
|
|
|
344
358
|
- `query.getClone()` - Get an exact copy of this query object
|
|
345
359
|
- `query.unjoin(tableName)` - Remove a join expression
|
|
346
|
-
- `query.reset(field)` - Reset a single aspect of the query (e.g. 'where' or '
|
|
347
|
-
|
|
360
|
+
- `query.reset(field)` - Reset a single aspect of the query (e.g. 'where' or '
|
|
361
|
+
having')
|
|
362
|
+
- `query.reset(fields)` - Reset a few particular aspects of the query (e.g.
|
|
363
|
+
`['where', 'having']`)
|
|
348
364
|
- `query.reset()` - Reset query to an empty state
|
|
349
365
|
|
|
350
366
|
### Parser limitations
|
|
@@ -428,7 +444,8 @@ import { SelectBuilder, getPagination } from 'sql-love';
|
|
|
428
444
|
const query = new SelectBuilder('SELECT * FROM users').limit(10).page(1);
|
|
429
445
|
// ...
|
|
430
446
|
// run a count query to determine that there are 42 results
|
|
431
|
-
const
|
|
447
|
+
const count = 42;
|
|
448
|
+
const pagination = getPagination(query, count);
|
|
432
449
|
expect(pagination).toEqual({
|
|
433
450
|
page: 1,
|
|
434
451
|
prevPage: null,
|
|
@@ -543,6 +560,7 @@ See [getPagination](#getpagination) for details on the pagination object.
|
|
|
543
560
|
```js
|
|
544
561
|
import { SelectBuilder, runMysql } from 'sql-love';
|
|
545
562
|
import { mysql } from 'mysql2';
|
|
563
|
+
|
|
546
564
|
const client = mysql.createConnection(config);
|
|
547
565
|
|
|
548
566
|
const query = new SelectBuilder('SELECT * FROM users');
|
|
@@ -557,6 +575,7 @@ found rows and pagination.
|
|
|
557
575
|
```js
|
|
558
576
|
import { SelectBuilder, runMysqlWithCount } from 'sql-love';
|
|
559
577
|
import { mysql } from 'mysql2';
|
|
578
|
+
|
|
560
579
|
const client = mysql.createConnection(config);
|
|
561
580
|
|
|
562
581
|
const query = new SelectBuilder('SELECT * FROM users')
|
|
@@ -572,6 +591,7 @@ You can also use the mysql2 promise api:
|
|
|
572
591
|
```js
|
|
573
592
|
import { SelectBuilder, runMysqlAsync, runMysqlAsyncWithCount } from 'sql-love';
|
|
574
593
|
import { mysql } from 'mysql2/promise';
|
|
594
|
+
|
|
575
595
|
const client = await mysql.createConnection(config);
|
|
576
596
|
|
|
577
597
|
const query = new SelectBuilder('SELECT * FROM users')
|
|
@@ -589,23 +609,23 @@ const { records, total, pagination } = await runMysqlAsyncWithCount(
|
|
|
589
609
|
|
|
590
610
|
### toSafeJson
|
|
591
611
|
|
|
592
|
-
With some database clients such as Prisma, your recordsets may contain BigInt
|
|
593
|
-
objects. These are not safe to pass to JSON.stringify()
|
|
594
|
-
examples of the toSafeJson
|
|
612
|
+
With some database clients such as Prisma, your recordsets may contain `BigInt`
|
|
613
|
+
objects. These are not safe to pass to `JSON.stringify()`. The following are
|
|
614
|
+
examples of the `toSafeJson*` utility functions.
|
|
595
615
|
|
|
596
616
|
```js
|
|
597
617
|
import { toSafeJsonString, toSafeJsonRecords } from 'sql-love';
|
|
598
618
|
|
|
599
|
-
const records = [{
|
|
619
|
+
const records = [{ postCount: 12n }, { postCount: 9007199254740993n }];
|
|
600
620
|
|
|
601
621
|
toSafeJsonRecords(records);
|
|
602
622
|
// [
|
|
603
|
-
// {
|
|
604
|
-
// {
|
|
623
|
+
// { postCount: 12 },
|
|
624
|
+
// { postCount: "9007199254740993" },
|
|
605
625
|
// ]
|
|
606
626
|
|
|
607
627
|
toSafeJsonString(records);
|
|
608
|
-
// '[{"
|
|
628
|
+
// '[{"postCount":12},{"postCount":"9007199254740993"}]'
|
|
609
629
|
|
|
610
|
-
//
|
|
630
|
+
// As you can see, BigInt values too big for Number are converted to strings
|
|
611
631
|
```
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
var m=Object.defineProperty;var L=Object.getOwnPropertyDescriptor;var U=Object.getOwnPropertyNames;var A=Object.prototype.hasOwnProperty;var j=(i,t)=>{for(var e in t)m(i,e,{get:t[e],enumerable:!0})},D=(i,t,e,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of U(t))!A.call(i,s)&&s!==e&&m(i,s,{get:()=>t[s],enumerable:!(n=L(t,s))||n.enumerable});return i};var F=i=>D(m({},"__esModule",{value:!0}),i);var P={};j(P,{Parser:()=>c,SelectBuilder:()=>d,extractGrouped:()=>S,extractIndexed:()=>E,extractLookup:()=>R,getPagination:()=>g,runMysql:()=>N,runMysqlAsync:()=>$,runMysqlAsyncWithCount:()=>I,runMysqlWithCount:()=>B,runPrisma:()=>x,runPrismaWithCount:()=>q,toSafeJsonRecord:()=>T,toSafeJsonRecords:()=>C,toSafeJsonString:()=>w,toSafeJsonValue:()=>p});module.exports=F(P);var c=class{query;constructor(t){this.query=t}_stripComments(t){return t=t.replace(/\/\*[\s\S]*?\*\//g,""),t=t.replace(/--([\r\n]|\s+[^\r\n]+[\r\n])/g,""),t=t.replace(/#[^\r\n]+[\r\n]/g,""),t}_extractSubQueries(t){let e={},n=0,s=r=>{let o=`~~SUBQUERY_${n++}~~`;return e[o]=r,o};return t=t.replace(/\(\s*SELECT\s+.+\)\s+AS\s+[^\s,]+/,s),t=t.replace(/\bIF\s*\(.+\)\s+AS\s+[^\s,]+/,s),t=t.replace(/\bIN\s*\(\s*SELECT\s.+?\)/,s),{sql:t,subQueries:e}}_injectSubQueries(t){let e=s=>t[s]||s,n=s=>s.replace(/~~SUBQUERY_\d+~~/g,e);this.query._columns=this.query._columns.map(e),this.query._joins=this.query._joins.map(n),this.query._wheres=this.query._wheres.map(n),this.query._havings=this.query._havings.map(n)}_split(t){let e=/(?:^|\s)(SELECT|FROM|(?:INNER|LEFT\s+OUTER|RIGHT\s+OUTER|LEFT|RIGHT|CROSS|FULL|FULL\s+OUTER)\s+JOIN|WHERE|GROUP\s+BY|HAVING|ORDER\s+BY|LIMIT|OFFSET|FETCH\s+NEXT)\b/i;return t.split(e)}parse(t){let e=this._stripComments(t),{sql:n,subQueries:s}=this._extractSubQueries(e),r=this._split(n),o=1;for(;o<r.length;){let l=r[o++].trim().toUpperCase().replace(/\s+/g,"_"),h=r[o++].trim(),a=`_handle${l}`;this[a](h)}return this._injectSubQueries(s),!0}_handleSELECT(t){let e=t.split(/s*,s*/),n="";e.forEach((s,r)=>{if(r===0){let o=/^(SQL_CALC_FOUND_ROWS)\s+/i,u=s.match(o);u&&(this.query.option(u[1]),s=s.replace(o,""))}n.length?s.indexOf(")")>0?(n+=s,this.query.column(n.trim()),n=""):n+=s+",":s.match(/\([^)]+$/)?n=s+",":this.query.column(s.trim())})}_handleFROM(t){t.split(/\s*,\s*/).forEach(n=>this.query.table(n))}_handleINNER_JOIN(t){this.query.innerJoin(t)}_handleLEFT_JOIN(t){this.query.leftJoin(t)}_handleLEFT_OUTER_JOIN(t){this.query.leftOuterJoin(t)}_handleRIGHT_JOIN(t){this.query.rightJoin(t)}_handleRIGHT_OUTER_JOIN(t){this.query.rightOuterJoin(t)}_handleCROSS_JOIN(t){this.query.crossJoin(t)}_handleFULL_JOIN(t){this.query.fullJoin(t)}_handleFULL_OUTER_JOIN(t){this.query.fullOuterJoin(t)}_handleWHERE(t){/^(1|'1'|true)$/i.test(t)?this.query._wheres.push(t):this._handleConditions("where",t)}_handleHAVING(t){this._handleConditions("having",t)}_handleConditions(t,e){e.split(/\bAND\b/i).forEach(s=>{let r=s.split(/\bOR\b/i).map(o=>o.trim());if(r.length===1){let o=t;this.query[o](r[0])}else{let o=t==="where"?"orWhere":"orHaving";this.query[o](r.map(u=>[u]))}})}_handleGROUP_BY(t){t.split(/\s*,\s*/).forEach(n=>this.query.groupBy(n))}_handleORDER_BY(t){t.split(/\s*,\s*/).forEach(n=>this.query.orderBy(n))}_handleLIMIT(t){let e=t.match(/^(\d+|\?|:\w+)\s*,\s*(\d+|\?|:\w+)$/);e?(this.query.offset(e[1]),this.query.limit(e[2])):(t=t.replace(/ ROWS$/i,""),this.query.limit(t))}_handleOFFSET(t){t=t.replace(/^(\d+) ROWS$/i,"$1"),this.query.offset(t)}_handleFETCH_NEXT(t){t=t.replace(/^(\d+) ROWS ONLY$/i,"$1"),this.query.limit(t)}};function b(i,t){let e=0,n=i.indexOf(t);for(;n!==-1;)e+=1,n=i.indexOf(t,n+1);return e}var _=i=>i==null||i===!1||isNaN(i),y="mysql",d=class i{_bindings=[];_options=[];_tables=[];_columns=[];_joins=[];_wheres=[];_groupBys=[];_havings=[];_orderBys=[];_limit;_offset;_page;constructor(t=null,e=null){if(t){if(e){let n=Object.keys(e),s=new RegExp(`:(${n.join("|")})`,"g");t=t.replace(s,(r,o)=>(this._bindings.push(e[o]),`__BOUND_${this._bindings.length-1}__`))}new c(this).parse(t)}}static setDefaultEngine(t){return y=t,i}compile({engine:t=y,bindingIndex:e=1}={}){let n=["SELECT",this._options.length?` ${this._options.join(`
|
|
2
|
+
`)}`:null,this._columns.length?` ${this._columns.join(`,
|
|
3
|
+
`)}`:` *
|
|
4
|
+
`,`FROM ${this._tables.join(", ")}`,this._joins.length?this._joins.join(`
|
|
5
|
+
`):null,this._wheres.length?`WHERE ${this._wheres.join(`
|
|
6
|
+
AND `)}`:null,this._groupBys.length?`GROUP BY ${this._groupBys.join(`,
|
|
7
|
+
`)}`:null,this._havings.length?`HAVING ${this._havings.join(`
|
|
8
|
+
AND `)}`:null,this._orderBys.length?`ORDER BY ${this._orderBys.join(`,
|
|
9
|
+
`)}`:null];if(this._page>0){let l=(this._page-1)*this._limit;n.push(this._compileOFFSET(t,l)),n.push(this._compileLIMIT(t,this._limit))}else this._offset&&n.push(this._compileOFFSET(t,this._offset)),this._limit&&n.push(this._compileLIMIT(t,this._limit));let s=n.filter(Boolean).join(`
|
|
10
|
+
`).trim(),r,o=[];if(t==="pg"){let l=e;r=(h,a)=>(o.push(this._bindings[a]),"$"+l++)}else r=(l,h)=>(o.push(this._bindings[h]),"?");return{sql:s.replace(/__BOUND_(\d+)__/g,r),bindings:o}}_compileLIMIT(t,e){if(t==="pg"||t==="mysql")return`LIMIT ${e}`;if(t==="mssql")return`FETCH NEXT ${e} ROWS ONLY`}_compileOFFSET(t,e){if(t==="pg"||t==="mysql")return`OFFSET ${e}`;if(t==="mssql"||t==="oracle")return`OFFSET ${e} ROWS`}compileCount({countExpr:t="*",engine:e=y,bindingIndex:n=1}={}){let s=this.getFoundRowsQuery(t),{sql:r,bindings:o}=s.compile({engine:e,bindingIndex:n});return this._havings.length===0?{sql:r,bindings:o}:{sql:`SELECT COUNT(*) AS found_rows
|
|
11
|
+
FROM (
|
|
12
|
+
${r.replace(/\n/g,`
|
|
13
|
+
`)}
|
|
14
|
+
) AS subquery_results`,bindings:o}}compileUnion(t,{engine:e=y,bindingIndex:n=1}={}){let s=this.compile({engine:e,bindingIndex:n}),r=t.compile({engine:e,bindingIndex:s.bindings.length+n});return{sql:`(${s.sql})
|
|
15
|
+
UNION
|
|
16
|
+
(${r.sql})`,bindings:[...s.bindings,...r.bindings]}}reset(t=null){if(Array.isArray(t))return t.forEach(e=>this.reset(e)),this;if(t){let e=["option","column","table","where","having","groupBy","orderBy"],n="_"+t.replace(/s$/,"");e.indexOf(t)>-1&&(n+="s");let s=["limit","offset","page"].indexOf(t)>-1;this[n]=s?null:[]}else this._options=[],this._columns=[],this._tables=[],this._joins=[],this._wheres=[],this._groupBys=[],this._havings=[],this._orderBys=[],this._limit=null,this._offset=null,this._page=null,this._bindings=[];return this}getClone(){let t=new i;return t._options=[...this._options],t._columns=[...this._columns],t._tables=[...this._tables],t._joins=[...this._joins],t._wheres=[...this._wheres],t._havings=[...this._havings],t._groupBys=[...this._groupBys],t._orderBys=[...this._orderBys],t._limit=this._limit,t._offset=this._offset,t._page=this._page,t._bindings=[...this._bindings],t}getFoundRowsQuery(t="*"){if(this._havings.length===0){let e=this.getClone();return e._columns=[`COUNT(${t}) AS found_rows`],e._options=[],e._groupBys=[],e._orderBys=[],e._limit=null,e._offset=null,e._page=null,e}else{let e=this.getClone();return e._limit=null,e._offset=null,e._page=null,e}}select(t){return this._columns=this._columns.concat(t),this}columns(t){return this._columns=this._columns.concat(t),this}column(t){return this._columns.push(t),this}option(t){return this._options.push(t),this}table(t){return this._tables.push(t),this}tables(t){return this._tables.push(...t),this}from(t){return this._tables.push(t),this}_addJoin(t,e,n=[]){n.length>0&&(e=e.replace(/\?/g,()=>(this._bindings.push(n.shift()),`__BOUND_${this._bindings.length-1}__`))),this._joins.push(`${t} ${e}`)}join(t,e=[]){return this._addJoin("JOIN",t,e),this}leftJoin(t,e=[]){return this._addJoin("LEFT JOIN",t,e),this}fullJoin(t,e=[]){return this._addJoin("FULL JOIN",t,e),this}rightJoin(t,e=[]){return this._addJoin("RIGHT JOIN",t,e),this}crossJoin(t,e=[]){return this._addJoin("CROSS JOIN",t,e),this}innerJoin(t,e=[]){return this._addJoin("INNER JOIN",t,e),this}leftOuterJoin(t,e=[]){return this._addJoin("LEFT OUTER JOIN",t,e),this}fullOuterJoin(t,e=[]){return this._addJoin("FULL OUTER JOIN",t,e),this}rightOuterJoin(t,e=[]){return this._addJoin("RIGHT OUTER JOIN",t,e),this}unjoin(t){return Array.isArray(t)?(t.forEach(e=>this.unjoin(e)),this):(this._joins=this._joins.filter(e=>!new RegExp(`JOIN ${t}\\b`).test(e)),this)}_conditions(t,e){let n=e.length,[s,r,o]=e;if(typeof s=="object"){for(let l of Object.entries(s))this._conditions(t,l);return this}if(n===1)return t.push(s),this;if(n===2&&Array.isArray(r)&&r.length>0&&b(s,"?")===r.length){let l=r,h=s.replace(/\?/g,()=>(this._bindings.push(l.shift()),`__BOUND_${this._bindings.length-1}__`));return t.push(h),this}else if(n===2){o=r;let l=s.split(" ");s=l.shift(),r=l.join(" ")}(typeof r!="string"||r==="")&&(r="="),r=r.toLocaleUpperCase();let u=r.match(/^(LIKE|NOT LIKE|ILIKE|NOT ILIKE)(?: (\?|\?%|%\?|%\?%))?$/i);if(r==="NOT BETWEEN"||r==="BETWEEN"){let l=this._bindings.length,h=this._bindings.length+1;t.push(`${s} ${r} __BOUND_${l}__ AND __BOUND_${h}__`),this._bindings.push(o[0],o[1])}else if(u){let l=u[1].toUpperCase(),h=u[2]||"?";if(Array.isArray(o)){let a=[];for(let J of o)a.push(`${s} ${l} __BOUND_${this._bindings.length}__`),this._bindings.push(h.replace("?",J));let f=a.join(" OR ");t.push(`(${f})`)}else t.push(`${s} ${l} __BOUND_${this._bindings.length}__`),this._bindings.push(h.replace("?",o||""))}else if(o===null)t.push(r==="="?`${s} IS NULL`:`${s} IS NOT NULL`);else if(Array.isArray(o)){let l=[];for(let a=0;a<o.length;a++)l.push(`__BOUND_${this._bindings.length}__`),this._bindings.push(o[a]);let h=l.join(", ");t.push(r==="="||r==="IN"?`${s} IN(${h})`:`${s} NOT IN(${h})`)}else r==="IN"||r==="NOT IN"?(t.push(`${s} ${r}(__BOUND_${this._bindings.length}__)`),this._bindings.push(o)):(t.push(`${s} ${r} __BOUND_${this._bindings.length}__`),this._bindings.push(o));return this}groupBy(t){return this._groupBys.push(t),this}where(...t){return this._conditions(this._wheres,t),this}whereBetween(t,e){return!_(e[0])&&!_(e[1])?this.where(t,"BETWEEN",e):!_(e[0])&&_(e[1])?this.where(t,">=",e[0]):_(e[0])&&!_(e[1])&&this.where(t,"<=",e[1]),this}_orConditions(t){let e=[];if(Array.isArray(t))for(let s of t)Array.isArray(s)?this._conditions(e,s):this._conditions(e,[s]);else for(let[s,r]of Object.entries(t)){let o=[];this._conditions(o,[s,r]),e.push(o[0])}let n=e.join(" OR ");return n.startsWith("(")&&n.endsWith(")")?n.slice(1,-1):n}orWhere(t){let e=this._orConditions(t);return this.where(`(${e})`),this}orHaving(t){let e=this._orConditions(t);return this.having(`(${e})`),this}having(...t){return this._conditions(this._havings,t),this}orderBy(t,e=null){if(e)this._orderBys.push(`${t} ${e}`);else{let n=t.replace(/^-(.+)/,"$1 DESC");this._orderBys.push(n)}return this}sortField(t,e={}){let n="ASC";t.startsWith("-")&&(n="DESC",t=t.slice(1));let s=e[t]||t;return this.orderBy(s,n),this}_isEntirelyDigits(t){return/^(0|[1-9]\d*)$/.test(t)}_isEntirelyDigitsNoZeros(t){return/^[1-9]\d*$/.test(t)}limit(t){if(typeof t=="string")t=t.trim(),this._isEntirelyDigitsNoZeros(t)&&(this._limit=parseInt(t,10));else if(typeof t=="number"&&Number.isInteger(t)&&t>=1)this._limit=t;else throw new Error(`SelectBuilder: "limit(n)" must receive a positive integer (got "${t}")`);return this}offset(t){if(typeof t=="string"&&this._isEntirelyDigits(t.trim()))this._offset=parseInt(t,10);else if(typeof t=="number"&&Number.isInteger(t)&&t>=0)this._offset=t;else throw new Error(`SelectBuilder: "offset(n)" must receive a positive integer (got "${t}")`);return this}page(t){if(typeof t=="string"&&this._isEntirelyDigitsNoZeros(t.trim()))this._page=parseInt(t,10);else if(typeof t=="number"&&Number.isInteger(t)&&t>=1)this._page=t;else throw new Error('SelectBuilder: "page(n)" must receive a positive integer');return this}};function S(i,t){let e=0,n={};for(let s of t){if(!(i in s))throw new Error(`Column "${String(i)}" is not present in record ${e}`);n[s[i]]||(n[s[i]]=[]),n[s[i]].push(s),e++}return n}function E(i,t){let e=0,n={};for(let s of t){if(!(i in s))throw new Error(`Column "${String(i)}" is not present in record ${e}`);n[s[i]]=s,e++}return n}function R(i,t,e){let n={},s=0;for(let r of e){if(!(i in r))throw new Error(`Column "${String(i)}" is not present in record ${s}`);if(!(t in r))throw new Error(`Column "${String(t)}" is not present in record ${s}`);s++,n[String(r[i])]=r[t]}return n}function g(i,t){t=Number(t)||0;let e=Number(i._limit)||null,n=null;t===0?n=null:i._page?n=Number(i._page):i._limit&&i._offset?n=Math.floor(Number(i._offset)/Number(i._limit))+1:i._limit&&!i._offset?n=1:n=null;let s=t===0||e===null?0:Math.ceil(t/e)||0,r=n===1,o=s>0&&n===s,u=s===0?null:n-1||null,l=s>0&&n+1<=s?n+1:null;return{page:n,prevPage:u,nextPage:l,perPage:e,numPages:s,total:t,isFirst:r,isLast:o}}function O(i,t,e){return new Promise((n,s)=>{i.query(t,e,(r,o,u)=>{if(r){s(r);return}n([o,u])})})}async function N(i,t){let{sql:e,bindings:n}=t.compile({engine:"mysql"});return O(i,e,n)}async function B(i,t,{countExpr:e="*"}={}){let{sql:n,bindings:s}=t.compile({engine:"mysql"}),[r,o]=await O(i,n,s);if(r.length>0){let{sql:u,bindings:l}=t.compileCount({engine:"mysql",countExpr:e}),[h]=await O(i,u,l),a=h[0].found_rows;return{records:r,total:a,pagination:g(t,a),fields:o}}else return{records:r,total:0,pagination:g(t,0),fields:o}}async function $(i,t){let{sql:e,bindings:n}=t.compile({engine:"mysql"});return i.query(e,n)}async function I(i,t,{engine:e="mysql",countExpr:n="*"}={}){let{sql:s,bindings:r}=t.compile({engine:e}),[o,u]=await i.query(s,r);if(o.length>0){let{sql:l,bindings:h}=t.compileCount({engine:e,countExpr:n}),[a]=await i.query(l,h),f=a[0].found_rows;return{records:o,total:f,pagination:g(t,f),fields:u}}else return{records:o,total:0,pagination:g(t,0),fields:u}}function p(i){if(typeof i=="bigint"){let t=Number(i);return i==t?t:i.toString()}return i}function T(i){let t={};for(let e in i)Object.prototype.hasOwnProperty.call(i,e)&&(t[e]=p(i[e]));return t}function C(i){return i.map(T)}function w(i){return JSON.stringify(i,(t,e)=>p(e))}async function x(i,t,{engine:e=void 0}={}){let{sql:n,bindings:s}=t.compile({engine:e});return i.$queryRawUnsafe(n,...s)}async function q(i,t,{engine:e=void 0,countExpr:n="*"}={}){let{sql:s,bindings:r}=t.compile({engine:e}),o=await i.$queryRawUnsafe(s,...r);if(o.length>0){let{sql:u,bindings:l}=t.compileCount({engine:e,countExpr:n}),h=await i.$queryRawUnsafe(u,...l),a=p(h[0].found_rows);return{records:o,total:a,pagination:g(t,a)}}else return{records:o,total:0,pagination:g(t,0)}}0&&(module.exports={Parser,SelectBuilder,extractGrouped,extractIndexed,extractLookup,getPagination,runMysql,runMysqlAsync,runMysqlAsyncWithCount,runMysqlWithCount,runPrisma,runPrismaWithCount,toSafeJsonRecord,toSafeJsonRecords,toSafeJsonString,toSafeJsonValue});
|