-
Notifications
You must be signed in to change notification settings - Fork 25
/
index.html
865 lines (813 loc) · 45.1 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
<meta http-equiv="X-UA-Compatible" content="chrome=1" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta name="viewport" content="target-densitydpi=device-dpi" />
<meta name="HandheldFriendly" content="true"/>
<link rel="canonical" href="https://csnw.github.io/sql-bricks" />
<title>SQL Bricks</title>
<style>
body {
font-size: 14px;
line-height: 22px;
background: #f4f4f4;
color: black;
font-family: Helvetica Neue, Helvetica, Arial;
}
.interface {
font-family: "Lucida Grande", "Lucida Sans Unicode", Helvetica, Arial, sans-serif !important;
}
code, pre, tt {
font-family: Monaco, Consolas, "Lucida Console", monospace;
font-size: 12px;
line-height: 18px;
font-style: normal;
}
tt {
padding: 0px 3px;
background: #fff;
border: 1px solid #ddd;
zoom: 1;
}
code {
margin-left: 20px;
}
pre {
font-size: 12px;
padding: 2px 0 2px 15px;
border-left: 5px solid #bbb;
margin: 0px 0 30px;
}
span.alias {
font-size: 14px;
font-style: italic;
margin-left: 20px;
}
b.header {
font-size: 16px;
line-height: 30px;
}
a.toc_title, a.toc_title:visited {
display: block;
color: black;
font-weight: bold;
margin-top: 15px;
}
a.toc_title:hover {
text-decoration: underline;
}
ul.toc_section {
font-size: 11px;
line-height: 14px;
margin: 5px 0 0 0;
padding-left: 0px;
list-style-type: none;
font-family: Lucida Grande;
}
.toc_section li {
cursor: pointer;
margin: 0 0 3px 0;
}
.toc_section li a {
text-decoration: none;
color: black;
}
.toc_section li a:hover {
text-decoration: underline;
}
div#sidebar {
background: white;
position: fixed;
top: 0; left: 0; bottom: 0;
width: 200px;
overflow-y: auto;
overflow-x: hidden;
-webkit-overflow-scrolling: touch;
padding: 15px 0 30px 30px;
border-right: 1px solid #bbb;
box-shadow: 0 0 20px #ccc; -webkit-box-shadow: 0 0 20px #ccc; -moz-box-shadow: 0 0 20px #ccc;
}
div.container {
width: 550px;
margin: 40px 0 50px 260px;
}
@media only screen and (-webkit-min-device-pixel-ratio: 1.5) and (max-width: 640px),
only screen and (-o-min-device-pixel-ratio: 3/2) and (max-width: 640px),
only screen and (min-device-pixel-ratio: 1.5) and (max-width: 640px) {
img {
max-width: 100%;
}
div#sidebar {
-webkit-overflow-scrolling: initial;
position: relative;
width: 90%;
height: 120px;
left: 0;
top: -7px;
padding: 10px 0 10px 30px;
border: 0;
}
div.container {
margin: 0;
width: 100%;
}
p, div.container ul {
max-width: 98%;
overflow-x: scroll;
}
pre {
overflow: scroll;
}
}
</style>
</head>
<body>
<div id="sidebar" class="interface">
SQL Bricks.js
<ul class="toc_section">
<li>» <a href="https://github.com/CSNW/sql-bricks">GitHub Repository</a></li>
</ul>
<a class="toc_title" href="#">
Introduction
</a>
<a class="toc_title" href="#use">
Use
</a>
<ul class="toc_section">
<li>- <a href="#sql">sql</a></li>
<li>- <a href="#val">val</a></li>
</ul>
<a class="toc_title" href="#statement">
Statement
</a>
<ul class="toc_section">
<li>- <a href="#clone">clone</a></li>
<li>- <a href="#toString">toString</a></li>
<li>- <a href="#toParams">toParams</a></li>
</ul>
<a class="toc_title" href="#select">
select
</a>
<ul class="toc_section">
<li>- <a href="#selconstructor">constructor</a></li>
<li>- <a href="#selselect">select</a></li>
<li>- <a href="#distinct">distinct</a></li>
<li>- <a href="#into">into</a></li>
<li>- <a href="#intoTemp">intoTemp</a></li>
<li>- <a href="#selfrom">from</a></li>
<li>- <a href="#join">join, leftJoin, rightJoin, fullJoin, crossJoin</a></li>
<li>- <a href="#on">on</a></li>
<li>- <a href="#using">using</a></li>
<li>- <a href="#naturaljoin">naturalJoin, naturalLeftJoin, naturalRightJoin, naturalFullJoin</a></li>
<li>- <a href="#selwhere">where</a></li>
<li>- <a href="#groupBy">groupBy</a></li>
<li>- <a href="#having">having</a></li>
<li>- <a href="#union">(union, intersect, minus, except)[All]</a></li>
<li>- <a href="#orderBy">orderBy</a></li>
<li>- <a href="#forUpdate">forUpdate, noWait</a></li>
</ul>
<a class="toc_title" href="#insert">
insert
</a>
<ul class="toc_section">
<li>- <a href="#insconstructor">constructor</a></li>
<li>- <a href="#insorReplace">orReplace, orRollback, orAbort, orFail, orIgnore</a></li>
<li>- <a href="#into">into</a></li>
<li>- <a href="#values">values</a></li>
<li>- <a href="#insselect">select</a></li>
<li>- <a href="#returning">returning</a></li>
</ul>
<a class="toc_title" href="#update">
update
</a>
<ul class="toc_section">
<li>- <a href="#updconstructor">constructor</a></li>
<li>- <a href="#updorReplace">orReplace, orRollback, orAbort, orFail, orIgnore</a></li>
<li>- <a href="#set">set</a></li>
<li>- <a href="#updwhere">where</a></li>
</ul>
<a class="toc_title" href="#delete">
delete
</a>
<ul class="toc_section">
<li>- <a href="#delconstructor">constructor</a></li>
<li>- <a href="#delfrom">from</a></li>
<li>- <a href="#using">using</a></li>
<li>- <a href="#delwhere">where</a></li>
</ul>
<a class="toc_title" href="#whereexpr">
Where Expressions
</a>
<ul class="toc_section">
<li>- <a href="#and">and</a></li>
<li>- <a href="#or">or</a></li>
<li>- <a href="#not">not</a></li>
<li>- <a href="#eq">eq, notEq, lt, lte, gt, gte</a></li>
<li>- <a href="#between">between</a></li>
<li>- <a href="#isNull">isNull, isNotNull</a></li>
<li>- <a href="#like">like</a></li>
<li>- <a href="#exists">exists</a></li>
<li>- <a href="#in">in</a></li>
<li>- <a href="#eqAll">eqAll, notEqAll, ltAll, lteAll, gtAll, gteAll</a></li>
<li>- <a href="#eqAny">eqAny, notEqAny, ltAny, lteAny, gtAny, gteAny</a></li>
</ul>
<a class="toc_title" href="#conveniences">
Conveniences
</a>
<ul class="toc_section">
<li>- <a href="#aliasExpansions">aliasExpansions</a></li>
<li>- <a href="#joinCriteria">joinCriteria</a></li>
</ul>
<a class="toc_title" href="#sqlfunctions">SQL Functions</a>
<a class="toc_title" href="#askeyword">AS Keyword</a>
<a class="toc_title" href="#contributing">Contributing</a>
<a class="toc_title" href="#acknowledgments">Acknowledgments</a>
<a class="toc_title" href="#license">License</a>
</div>
<div class="container">
<h1>SQL Bricks.js</h1>
<p><a href="https://travis-ci.org/CSNW/sql-bricks" title="Build Status"><img src="https://travis-ci.org/CSNW/sql-bricks.png?branch=master"></a></p>
<p id="introduction">SQL Bricks was created to help eliminate DRY violations in SQL-heavy applications. SQL statements can be easily stored, cloned, modified and passed around to other parts of an application and they can generate both parameterized and non-parameterized SQL.</p>
<p>In addition, SQL Bricks contains a few conveniences to aid in re-use and to make SQL generation a little less of a chore: automatic quoting of columns that collide with keywords (<tt>order</tt>, <tt>desc</tt>, etc) & columns that contain capital letters, automatic <a href="#aliasExpansions">alias expansion</a>, user-supplied <a href="#joinCriteria">join criteria</a> functions.</p>
<p>SQL Bricks differs from similar libraries in that it does not require a schema and it is designed to be transparent, matching SQL so faithfully that developers with knowledge of SQL will pick up the API extremely quickly.</p>
<p>The core SQL Bricks library supports the four CRUD statements (<tt>SELECT</tt>, <tt>INSERT</tt>, <tt>UPDATE</tt>, <tt>DELETE</tt>) and all of their clauses as defined by <b><a href="https://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt">SQL-92</a></b> (additional clauses supported by <b>Postgres</b>, <b>MySQL</b>, and <b>SQLite</b> are in the dialect-specific extension libraries, <a href="https://github.com/Suor/sql-bricks-postgres">sql-bricks-postgres</a>, <a href="https://github.com/tamarzil/mysql-bricks">mysql-bricks</a> and <a href="https://github.com/CSNW/sql-bricks-sqlite">sql-bricks-sqlite</a>, respectively). Adding support for other SQL statements (<tt>CREATE</tt>, <tt>ALTER TABLE</tt>, etc) would clutter the library without providing much real benefit.</p>
<p>The <a href="https://github.com/CSNW/sql-bricks">source</a> is on GitHub and over <a href="browser-tests.html">200 tests</a> are available for your perusal.</p>
<h2>Related Libraries</h2>
<p><a href="https://github.com/tamarzil/mysql-bricks">mysql-bricks</a> adds mysql-dialect extensions, like <tt>INSERT ... ON DUPLICATE KEY UPDATE ...</tt>, <tt>INSERT IGNORE ...</tt>, <tt>LIMIT (SELECT / UPDATE / DELETE)</tt>, <tt>OFFSET</tt>, <tt>ORDER BY (UPDATE / DELETE)</tt></p>
<p><a href="https://github.com/CSNW/sql-bricks-sqlite">sql-bricks-sqlite</a> adds sqlite-dialect extensions, like <tt>LIMIT ... OFFSET</tt> and <tt>OR REPLACE</tt></p>
<p><a href="https://github.com/Suor/sql-bricks-postgres">sql-bricks-postgres</a> adds postgres-dialect extensions, like <tt>LIMIT ... OFFSET</tt> and <tt>RETURNING</tt></p>
<p><a href="https://github.com/Suor/pg-bricks">pg-bricks</a> adds postgres connections, transactions, query execution and data accessors on top of SQLBricks.</p>
<div id="documentation">
<h2 id="use">Use</h2>
<p>
<p>In <a href="https://nodejs.org">node</a>, SQL Bricks can be accessed with <tt>require('sql-bricks')</tt>. In the browser, it can be accessed with the <tt>SqlBricks</tt> global. SQL Bricks has no production dependencies; its only dev dependency is Mocha.js. Throughout this documentation, it is assumed that SQL Bricks is set to a local <tt>sql</tt> variable and that top-level functions are also set to local variables (for example: <tt>var sql = SqlBricks, select = sql.select, or = sql.or, $in = sql.in;</tt>).</p>
<p>The API is designed to mirror SQL faithfully, with SQL keywords represented by chainable camelCase method and non-keywords passed as string arguments. To make the API easier to use, objects can also be passed in wherever there are key/value pairs.</p>
<pre>
select().from('person')
.where(or({last_name: 'Rubble'}, $in('first_name', ['Fred', 'Wilma', 'Pebbles'])));
// SELECT * FROM person
// WHERE last_name = 'Rubble' OR first_name IN ('Fred', 'Wilma', 'Pebbles')
</pre>
</p>
<p id="sql">
<b class="header">sql</b><code>sql(str[, values])</code>
<br />
<p>The SQL Bricks namespace (saved to the local variable <tt>sql</tt> in these docs) can be called as a function to insert SQL into SQL Bricks somewhere that a value is expected (the right-hand side of <tt>WHERE</tt> criteria, or <tt>insert()</tt>/<tt>update()</tt> values):
<pre>
select('*').from('person').where({'billing_addr_id': sql('mailing_addr_id')})
// SELECT * FROM person WHERE billing_addr_id = mailing_addr_id
</pre>
</p>
<p>Multiple <b>values</b> can be passed in as separate arguments or as an array (this example uses the postgres <a href="https://www.postgresql.org/docs/9.4/static/functions-json.html#FUNCTIONS-JSONB-OP-TABLE">jsonb contains operator</a>, <tt>@></tt>):
<pre>
select().where(sql('field @> $ and field @> $', { key: 'value' }, { key: 'value2' })).toParams()
// {"text": "SELECT * WHERE field @> $1 and field @> $2", "values": [{"key": "value"}, {"key": "value2"}]}
</pre>
</p>
<p>Note that numbered params can be used in the literal SQL string and these refer to the position in the current `sql()` call, not the position in the statement as a whole:
<pre>
select().where({name: 'Fred'}).and(sql('f1 @> $2 and f2 @> $1', [{key: 'value' }, {key: 'value2'}])).toParams()
// {"text": "SELECT * WHERE name = $1 AND f1 @> $3 and f2 @> $2", "values": ["Fred", {"key": "value"}, {"key": "value2"}]}
</pre>
</p>
<p id="val">
<b class="header">val</b><code>sql.val(value)</code>
<br />
<p>Wraps a <b>value</b> (user-supplied string, number, boolean, etc) so that it can be passed into SQL Bricks anywhere that a column is expected (the left-hand side of <tt>WHERE</tt> criteria and many other SQL Bricks APIs):</p>
<pre>
select().from('person').where(sql.val('Fred'), sql('first_name'));
// SELECT * FROM person WHERE 'Fred' = first_name
</pre>
</p>
<h2 id="statement">Statement</h2>
<p>
<tt>Statement</tt> is an abstract base class for all statements (<tt>SELECT, INSERT, UPDATE, DELETE</tt>) and should never be instantiated directly. It is exposed because it can be used with the <tt>instanceof</tt> operator to easily determine whether something is a SQL Bricks statement: <tt>my_var instanceof Statement</tt>.
</p>
<p id="clone">
<b class="header">clone</b><code>stmt.clone()</code>
<br />
Clones a statement so that subsequent modifications do not affect the original statement.
<pre>
var active_persons = select('*').from('person').where({'active': true});
// SELECT * FROM person WHERE active = TRUE
var local_persons = active_persons.clone().where({'local': true});
// SELECT * FROM person WHERE active = TRUE AND local = TRUE
</pre>
</p>
<p id="toString">
<b class="header">toString</b><code>stmt.toString()</code>
<br />
Returns the non-parameterized SQL for the statement. This is called implicitly by Javascript when using a Statement anywhere that a string is expected (string concatenation, <tt>Array.join()</tt>, etc).
<br />
While <tt>toString()</tt> is easy to use, it is not recommended in most cases because:
<ul><li>It doesn't provide <a href="https://en.wikipedia.org/wiki/SQL_injection#Parameterized_statements">robust protection</a> against SQL injection attacks (it just does basic escaping)</li>
<li>It doesn't provide as much support for complex data types (objects, arrays, etc, are "stringified" before being passed to your database driver, which then has to interpret them correctly)</li>
<li>It does not provide the same level of detail in error messages (see <a href="https://github.com/Suor/sql-bricks-postgres/issues/10">this issue</a>)</li></ul>
For the above reasons, it is usually better to use <tt>toParams()</tt>.
</p>
<p id="toParams">
<b class="header">toParams</b><code>stmt.toParams(options)</code>
<br />
<p>Returns an object with two properties: a parameterized <tt>text</tt> string and a <tt>values</tt> array. The values are populated with anything on the right-hand side of a <tt>WHERE</tt> criteria, as well as any values passed into an <tt>insert()</tt> or <tt>update()</tt> (they can be passed explicitly with <tt>val()</tt> or opted out of with <tt>sql()</tt>):</p>
<pre>
update('person', {'first_name': 'Fred'}).where({'last_name': 'Flintstone'}).toParams();
// {"text": "UPDATE person SET first_name = $1 WHERE last_name = $2", "values": ["Fred", "Flintstone"]}
</pre>
<p>A <tt>placeholder</tt> <b>option</b> of <tt>'?%d'</tt> can be passed to generate placeholders compatible with <a href="https://github.com/developmentseed/node-sqlite3">node-sqlite3</a> (<tt>%d</tt> is replaced with the parameter #):</p>
<pre>
update('person', {'first_name': 'Fred'}).where({'last_name': 'Flintstone'}).toParams({placeholder: '?%d'});
// {"text": "UPDATE person SET first_name = ?1 WHERE last_name = ?2", "values": ["Fred", "Flintstone"]}
</pre>
<p>Or <a href="https://github.com/felixge/node-mysql">node-mysql</a>:</p>
<pre>
update('person', {'first_name': 'Fred'}).where({'last_name': 'Flintstone'}).toParams({placeholder: '?'});
// {"text": "UPDATE person SET first_name = ? WHERE last_name = ?", "values": ["Fred", "Flintstone"]}
</pre>
</p>
<h2 id="select">select</h2>
<p id="selconstructor">
<b class="header">constructor</b><code>select(columns)</code>
<br />
<p><tt>sql.select()</tt> returns a new select statement, seeded with a set of <b>columns</b>. It can be used with or without the <tt>new</tt> keyword. Columns can be passed in here (or appended later via <tt>sel.select()</tt> or <tt>sel.distinct()</tt>) via multiple arguments or a comma-delimited string or an array.</p>
<p>If no <b>columns</b> are specified, <tt>toString()</tt> will default to <tt>SELECT *</tt>.</p>
</p>
<p id="selselect">
<b class="header">select</b><code>sel.select(columns)</code>
<br />
Appends additional <b>columns</b> to an existing query. Columns can be passed as multiple arguments, a comma-delimited string or an array.
</p>
<p id="distinct">
<b class="header">distinct</b><code>sel.distinct(columns)</code>
<br />
Makes the query a <tt>SELECT DISTINCT</tt> query. For convenience, any <b>columns</b> passed will be appended to the query (they can be passed in the same ways as to <tt>select()</tt>).
</p>
<p id="into">
<b class="header">into</b><code>sel.into(tbl)</code>
<span class="alias">Alias: <b>intoTable</b></span>
<br />
<p>Makes the query a <tt>SELECT ... INTO</tt> query (which creates a new table with the results of the query).</p>
</p>
<p id="intoTemp">
<b class="header">intoTemp</b><code>sel.intoTemp(tbl)</code>
<span class="alias">Alias: <b>intoTempTable</b></span>
<br />
<p>Does the same as <tt>.into()</tt>, but with the addition of the <tt>TEMP</tt> keyword, making it a temporary table.</p>
</p>
<p id="selfrom">
<b class="header">from</b><code>sel.from(tbls)</code>
<br />
Table names can be passed in as multiple string arguments, a comma-delimited string or an array.
</p>
<p id="join">
<b class="header">join, leftJoin, rightJoin, fullJoin, crossJoin</b><br />
<code>sel.join(tbl[, onCriteria])</code><br />
<span class="alias">Aliases: <b>innerJoin, leftOuterJoin, rightOuterJoin, fullOuterJoin</b></span>
<br />
<p>Adds the specified join to the query. <b>tbl</b> can include an alias after a space or after the <tt>'AS'</tt> keyword (<tt>'my_table my_alias'</tt>). <b>onCriteria</b> is optional if a <a href="#joinCriteria">joinCriteria</a> function has been supplied.</p>
<p><i>Note: <b>crossJoin</b> will ignore any <b>onCriteria</b> argument, if supplied.</i></p>
<pre>
select().from('person').join('address', {'person.addr_id': 'address.id'});
// SELECT * FROM person INNER JOIN address ON person.addr_id = address.id
</pre>
</p>
<p id="on">
<b class="header">on</b><code>sel.on(onCriteria)</code>
<br />
<p><i>Note: <b>onCriteria</b> can also be passed as the second argument to <tt>join</tt>.</i></p>
<pre>
select('*').from('person').innerJoin('address').on('person.addr_id', 'address.id');
// SELECT * FROM person INNER JOIN address ON person.addr_id = address.id
select('*').from('person').join('address').on({'person.addr_id': 'address.id'});
// SELECT * FROM person INNER JOIN address ON person.addr_id = address.id
</pre>
</p>
<p id="using">
<b class="header">using</b><code>sel.using(columnList)</code>
<br />
<p>Joins using <tt>USING</tt> instead of <tt>ON</tt>. <b>columnList</b> can be passed in as one or more string arguments, a comma-delimited string, or an array.</p>
<pre>
select('*').from('person').innerJoin('address').using('address_id');
// SELECT * FROM person INNER JOIN address USING (address_id)
select('*').from('person').join('address').using('address_id', 'country_id');
// SELECT * FROM person INNER JOIN address USING (address_id, country_id)
select('*').from('person').join('address').using('address_id, country_id');
// SELECT * FROM person INNER JOIN address USING (address_id, country_id)
select('*').from('person').join('address').using(['address_id', 'country_id']);
// SELECT * FROM person INNER JOIN address USING (address_id, country_id)
</pre>
<p><i>Note: <b>columnList</b> can also be passed as the second argument to <tt>join</tt> - in which case it must be specified <b>as an array</b> of one or more string values.</i></p>
<pre>
select('*').from('person').join('address', ['address_id', 'country_id']);
// SELECT * FROM person INNER JOIN address USING (address_id, country_id)
</pre>
</p>
<p id="naturaljoin">
<b class="header">naturalJoin, naturalLeftJoin, naturalRightJoin, naturalFullJoin</b><br />
<code>sel.naturalJoin(tbl)</code><br />
<span class="alias">Aliases: <b>naturalInnerJoin, naturalLeftOuterJoin, naturalRightOuterJoin, naturalFullOuterJoin</b></span>
<br />
<p>Adds the specified natural join to the query. <b>tbl</b> can include an alias after a space or after the <tt>'AS'</tt> keyword (<tt>'my_table my_alias'</tt>).</p>
<pre>
select().from('person').naturalJoin('address');
// SELECT * FROM person NATURAL INNER JOIN address
</pre>
</p>
<p id="selwhere">
<b class="header">where</b><span class="alias">Alias: <b>and</b></span><br />
<code>sel.where(column, value)</code><br />
<code>sel.where(criteria)</code><br />
<code>sel.where(whereExpr)</code><br />
<code>sel.where(sql)</code>
<p>Sets or extends the <tt>WHERE</tt> clause. If there already is a <tt>WHERE</tt> clause, the new criteria will be added to a top-level <tt>AND</tt> group.
<pre>
select('*').from('person').where('first_name', 'Fred');
// SELECT * FROM person WHERE first_name = 'Fred'
select('*').from('person').where('last_name', 'Flintstone').and('first_name', 'Fred');
// SELECT * FROM person WHERE last_name = 'Flintstone' AND first_name = 'Fred'
</pre>
<p>Multiple key/value pairs in a <b>criteria</b> object are <tt>AND</tt>ed together:</p>
<pre>
select('*').from('person').where({'last_name': 'Flintstone', 'first_name': 'Fred'});
// SELECT * FROM person WHERE last_name = 'Flintstone' AND first_name = 'Fred'
</pre>
<p>For nested groupings and for relational operators beyond <tt>=</tt>, a <a href="#whereexpr"><b>whereExpr</b></a> can be passed in:</p>
<pre>
select('*').from('person').where(or(like('last_name', 'Flint%'), {'first_name': 'Fred'}));
// SELECT * FROM person WHERE last_name LIKE 'Flint%' OR first_name = 'Fred'
</pre>
<p>If necessary, literal SQL can be passed in via <a href="#sql"><tt>sql(str[, values...])</tt></a>:</p>
<pre>
var last_name = 'Flintstone';
select('*').from('person').where(sql('LTRIM(last_name) = $', last_name));
// SELECT * FROM person WHERE LTRIM(last_name) = 'Flintstone'
</pre>
</p>
<p id="groupBy">
<b class="header">groupBy</b><code>sel.groupBy(columns)</code>
<span class="alias">Alias: <b>group</b></span>
<br />
Sets or extends the <tt>GROUP BY</tt> columns. Can take multiple arguments, a single comma-delimited string or an array.
</p>
<p id="having">
<b class="header">having</b><br />
<code>sel.having(column, value)</code><br />
<code>sel.having(criteria)</code><br />
<code>sel.having(whereExpr)</code>
<p>Sets or extends the <tt>HAVING</tt> criteria. Like <tt>where()</tt>, it takes a <b>column</b> / <b>value</b> pair, a <b>criteria</b> object or a <a href="#whereexpr"><b>whereExpr</b></a>:</p>
<pre>
select('city', 'max(temp_lo)').from('weather')
.groupBy('city').having(lt('max(temp_lo)', 40))
// SELECT city, max(temp_lo) FROM weather
// GROUP BY city HAVING max(temp_lo) < 40
</pre>
</p>
<p id="union">
<b class="header">(union, intersect, minus, except)[All]</b><code>sel.union([stmt, ...])</code>
<br />
<p>Creates a compound statement by fusing together two or more queries, returns the last statement. If no <b>stmt</b> is passed, a new <tt>select()</tt> will be created and returned:</p>
<pre>
select().from('person').where({'last_name': 'Flintstone'}).union()
.select().from('person').where({'last_name': 'Rubble'});
// SELECT * FROM person WHERE last_name = 'Flintstone' UNION
// SELECT * FROM person WHERE last_name = 'Rubble'
</pre>
</p>
<p id="orderBy">
<b class="header">orderBy</b><code>sel.orderBy(columns)</code>
<span class="alias">Alias: <b>order</b></span>
<br />
<p>Sets or extends the list of columns in the <tt>ORDER BY</tt> clause. Columns can be passed as multiple arguments, a single comma-delimited string or an array.</p>
</p>
<p id="forUpdate">
<b class="header">forUpdate, noWait</b><code>sel.forUpdate([tbl, ...]) / sel.noWait()</code>
<br />
<p>Add the <tt>FOR UPDATE</tt> clause to lock all selected records from all tables in the select (or just the tables specified), along with an optional <tt>NOWAIT</tt> at the end:</p>
<pre>
select('addr_id').from('person').forUpdate().of('addr_id').noWait();
// SELECT addr_id FROM person FOR UPDATE OF addr_id NOWAIT
</pre>
</p>
<h2 id="insert">insert</h2>
<p id="insconstructor">
<b class="header">constructor</b><span class="alias">Alias: <b>insertInto</b></span>
<code>insert(tbl[, values])</code><br />
<code>insert(tbl[, columns])</code>
<br />
<p><tt>sql.insert()</tt> returns a new <tt>INSERT</tt> statement. It can be used with or without the <tt>new</tt> operator.</p>
<pre>
insert('person', {'first_name': 'Fred', 'last_name': 'Flintstone'});
// INSERT INTO person (first_name, last_name) VALUES ('Fred', 'Flintstone')
</pre>
<p>It can take a <b>values</b> object or a <b>columns</b> list. Passing a set of columns (as multiple arguments, a comma-delimited string or an array) will put the statement into <tt>split keys/values</tt> mode, where a matching array of values is expected in <tt>values()</tt>:</p>
<pre>
insertInto('person', 'first_name', 'last_name').values('Fred', 'Flintstone');
// INSERT INTO person (first_name, last_name) VALUES ('Fred', 'Flintstone')
</pre>
</p>
<p id="values">
<b class="header">values</b><code>ins.values(values)</code>
<br />
<p><b>values</b> can be an object of key/value pairs or a set of values matching a set of keys passed into <tt>insert()</tt>:
<pre>
insertInto('person', 'first_name', 'last_name').values('Fred', 'Flintstone');
// INSERT INTO person (first_name, last_name) VALUES ('Fred', 'Flintstone')
insertInto('person').values({'first_name': 'Fred', 'last_name': 'Flintstone'});
// INSERT INTO person (first_name, last_name) VALUES ('Fred', 'Flintstone')
</pre>
<p><b>values</b> can also be an array of objects or an array of arrays matching a set of keys passed into <tt>insert()</tt>:
<pre>
insertInto('person', 'first_name', 'last_name').values([['Fred', 'Flintstone'], ['Wilma', 'Flintstone']]);
// INSERT INTO person (first_name, last_name) VALUES ('Fred', 'Flintstone'), ('Wilma', 'Flintstone')
insertInto('person').values([{'first_name': 'Fred', 'last_name': 'Flintstone'}, {'first_name': 'Wilma', 'last_name': 'Flintstone'}]);
// INSERT INTO person (first_name, last_name) VALUES ('Fred', 'Flintstone'), ('Wilma', 'Flintstone')
</pre>
<p><i>Note: <b>values</b> can also be passed as the second argument to <tt>insert()</tt>.</i></p>
</p>
<p id="insselect">
<b class="header">select</b><code>ins.select(columns)</code>
<br />
<p>Returns a new <tt>SELECT</tt> statement that is connected to the <tt>INSERT</tt> statement, allowing chaining of select methods. When <tt>toString() / toParams()</tt> is called, it will render them both statements together as an <tt>INSERT INTO ... SELECT</tt> statement.</p>
<p>Note that if you want to call a method on the insert object, you have to save a reference to it before calling <tt>select()</tt>, since <tt>select()</tt> returns a reference to a child Select statement instead of the parent Insert statement.</p>
</p>
<h2 id="update">update</h2>
<p id="updconstructor">
<b class="header">constructor</b><code>update(tbl[, values])</code>
<br />
<p><tt>sql.update()</tt> returns a new <tt>UPDATE</tt> statement. It can be used with or without the <tt>new</tt> operator.</p>
<pre>
update('person', {'first_name': 'Fred', 'last_name': 'Flintstone'});
// UPDATE person SET first_name = 'Fred', last_name = 'Flintstone'
</pre>
<p id="set">
<b class="header">set</b><span class="alias">Alias: <b>values</b></span><br />
<code>upd.set(column, value)</code><br />
<code>upd.set(values)</code>
<br />
<p>Sets or extends the values of the <tt>UPDATE</tt> statement with a <b>column, value</b> pair or a <b>values</b> object.</p>
<pre>
update('person').set('first_name', 'Fred').set('last_name', 'Flintstone');
// UPDATE person SET first_name = 'Fred', last_name = 'Flintstone'
update('person').set({'first_name': 'Fred', 'last_name': 'Flintstone'});
// UPDATE person SET first_name = 'Fred', last_name = 'Flintstone'
</pre>
<p><i>Note: <b>values</b> can also be passed as the second argument to <tt>update()</tt>.</i></p>
</p>
<p id="updwhere">
<b class="header">where</b><span class="alias">Alias: <b>and</b></span><br />
<code>upd.where(column, value)</code><br />
<code>upd.where(criteria)</code><br />
<code>upd.where(whereExpr)</code>
<br />
<p>Sets or extends the <tt>WHERE</tt> clause. See <a href="#selwhere">select.where()</a> for more details.</p>
</p>
<h2 id="delete">delete</h2>
<p id="delconstructor">
<b class="header">constructor</b><code>delete(tbl)</code><span class="alias">Alias: <b>deleteFrom</b></span>
<br />
<p><tt>sql.delete()</tt> returns a new <tt>DELETE</tt> statement. It can be used with or without the <tt>new</tt> operator.</p>
</p>
<p id="delfrom">
<b class="header">from</b><code>del.from(tbl)</code>
<br />
<p>Convenience method to make the delete API match the select API more closely.</p>
</p>
<p id="using">
<b class="header">using</b><code>del.using(tbl[, ...])</code>
<br />
<p>Sets or extends the list of tables in the <tt>USING</tt> clause. This is <tt>DELETE</tt>'s equivalent of <tt>SELECT</tt>'s multi-table <tt>FROM</tt> clause.
</p>
<p id="delwhere">
<b class="header">where</b><span class="alias">Alias: <b>and</b></span><br />
<code>del.where(column, value)</code><br />
<code>del.where(criteria)</code><br />
<code>del.where(whereExpr)</code>
<br />
<p>Sets or extends the <tt>WHERE</tt> clause. See <a href="#selwhere">select.where()</a> for more details.</p>
</p>
<h2 id="whereexpr">Where Expressions</h2>
<p id="object">
<b class="header">Object</b><code>{ ... }</code>
<br />
<p>When a non-expression object is passed somewhere a <tt>whereExpression</tt> is expected, each key/value pair will be <tt>AND</tt>ed together:</p>
<pre>
select().from('person').where({'first_name': 'Fred', 'last_name': 'Flintstone'});
// SELECT * FROM person WHERE first_name = 'Fred' AND last_name = 'Flintstone'
</pre>
</p>
<p id="and">
<b class="header">and</b><code>and(whereExpr[, ...])</code>
<br />
<p>Joins the passed expressions with <tt>AND</tt></p>
</p>
<p id="or">
<b class="header">or</b><code>or(whereExpr[, ...])</code>
<br />
<p>Joins the passed expressions with <tt>OR</tt>:</p>
<pre>
select().from('person').where(or(like('last_name', 'Flint%'), {'first_name': 'Fred'}));
// SELECT * FROM person WHERE last_name LIKE 'Flint%' OR first_name = 'Fred'
</pre>
<p>An object with multiple key/value pairs will generate an <tt>OR</tt>:</p>
<pre>
select().from('person').where(or({'first_name': 'Fred', 'last_name': 'Rubble'}));
// SELECT * FROM person WHERE first_name = 'Fred' OR last_name = 'Rubble'
</pre>
</p>
<p id="not">
<b class="header">not</b><code>not(whereExpr)</code>
<br />
<p>Negates the expression by wrapping it in <tt>NOT (...)</tt> (if it is at the top level, the parentheses are unnecessary and will be omitted):</p>
<pre>
select().from('person').where(not($in('name', ['Fred', 'Barney', 'Wilma', 'Pebbles'])));
// SELECT * FROM person WHERE NOT name IN ('Fred', 'Barney', 'Wilma', 'Pebbles')
</pre>
</p>
<p id="eq">
<b class="header">eq, notEq, lt, lte, gt, gte</b><br />
<code>eq(column, value)</code><br />
<code>notEq(column, value)</code><br />
<code>lt(column, value)</code><br />
<code>lte(column, value)</code><br />
<code>gt(column, value)</code><br />
<code>gte(column, value)</code>
<br />
<p>Generates the appropriate relational operator (<tt>=</tt>, <tt><></tt>, <tt><</tt>, <tt><=</tt>, <tt>></tt> or <tt>>=</tt>).</p>
<pre>
select().from('person').where(gt('access', 5));
// SELECT * FROM person WHERE access > 5
</pre>
</p>
<p id="between">
<b class="header">between</b><code>between(column, value1, value2)</code>
<br />
<p>Generates a <tt>BETWEEN</tt>:</p>
<pre>
select().from('person').where(between('access', 1, 5));
// SELECT * FROM person WHERE access BETWEEN 1 AND 5
</pre>
</p>
<p id="isNull">
<b class="header">isNull, isNotNull</b><code>isNull(column), isNotNull(column)</code>
<br />
<p>Generates <tt>IS NULL</tt> and <tt>IS NOT NULL</tt> expressions:</p>
<pre>
select().from('person').where(isNull('name'));
// SELECT * FROM person WHERE name IS NULL
</pre>
</p>
<p id="like">
<b class="header">like</b><code>like(column, value)</code>
<br />
<p>Generates a <tt>LIKE</tt> expression.</p>
<pre>
select('*').from('person').where(or(like('last_name', 'Flint%'), {'first_name': 'Fred'}));
// SELECT * FROM person WHERE last_name LIKE 'Flint%' OR first_name = 'Fred'
</pre>
</p>
<p id="exists">
<b class="header">exists</b><code>exists(stmt)</code>
<br />
<p>Takes a <b>stmt</b> subquery and generates an <tt>EXISTS</tt> expression:</p>
<pre>
select().from('person').where(exists(
select().from('address').where({'address.id': sql('person.addr_id')})));
// SELECT * FROM person WHERE EXISTS
// (SELECT * FROM address WHERE address.id = person.addr_id)
</pre>
</p>
<p id="in">
<b class="header">in</b><br />
<code>in(column, values)</code><br />
<code>in(column, stmt)</code>
<br />
<p>Generates an <tt>IN</tt> expression based on a set of <b>values</b> (can be an array or multiple arguments) or a <b>stmt</b> subquery:</p>
<pre>
select().from('person').where($in('first_name', 'Fred', 'Barney', 'Wilma'));
// SELECT * FROM person WHERE first_name IN ('Fred', 'Barney', 'Wilma')
select().from('person').where($in('addr_id', select('id').from('address')));
// SELECT * FROM person WHERE addr_id IN (SELECT id FROM address)
</pre>
</p>
<p id="eqAll">
<b class="header">eqAll, notEqAll, ltAll, lteAll, gtAll, gteAll</b><br />
<code>eqAll(stmt), notEqAll(stmt), ltAll(stmt), lteAll(stmt), gtAll(stmt), gteAll(stmt)</code>
<br />
<p>Pairs the appropriate relational operator with the <tt>ALL</tt> keyword.</p>
</p>
<p id="eqAny">
<b class="header">eqAny, notEqAny, ltAny, lteAny, gtAny, gteAny</b><br />
<code>eqAny(stmt), notEqAny(stmt), ltAny(stmt), lteAny(stmt), gtAny(stmt), gteAll(stmt)</code><br />
<span class="alias">Alias: <b>eqSome, notEqSome, ltSome, lteSome, gtSome, gteSome</b></span>
<br />
<p>Pairs the appropriate relational operator with the <tt>ANY</tt> keyword:</p>
<pre>
select().from('person').where(eqAny('person.id', select('person_id').from('address')));
// SELECT * FROM person WHERE person.id = ANY (SELECT person_id FROM address)
</pre>
</p>
<h2 id="conveniences">Conveniences</h2>
<p id="aliasExpansions">
<b class="header">aliasExpansions</b><code>sql.aliasExpansions(expansions)</code>
<br />
Registers a set of frequently-used table aliases with SQL Bricks. These table aliases can then be used by themselves in <tt>from()</tt>, <tt>join()</tt>, etc and SQL Bricks will automatically expand them to include the table name as well as the alias:
<pre>
sql.aliasExpansions({'psn': 'person', 'addr': 'address', 'zip': 'zipcode', 'usr': 'user'});
select().from('psn').join('addr', {'psn.addr_id': 'addr.id'});
// SELECT * FROM person psn INNER JOIN address addr ON psn.addr_id = addr.id
</pre>
</p>
<p id="joinCriteria">
<b class="header">joinCriteria</b><code>sql.joinCriteria(func)</code>
<br />
<p>Sets a user-supplied function to automatically generate the <tt>.on()</tt> criteria for joins whenever it is not supplied explicitly:</p>
<pre>
var alias_expansions = {'psn': 'person', 'addr': 'address', 'zip': 'zipcode', 'usr': 'user'};
var table_to_alias = invert(alias_expansions);
sql.joinCriteria(function(left_tbl, left_alias, right_tbl, right_alias) {
var criteria = {};
criteria[left_alias + '.' + table_to_alias[right_tbl] + '_id'] = right_alias + '.id';
return criteria;
});
select().from('person').join('address');
// SELECT * FROM person INNER JOIN address ON person.addr_id = address.id
</pre>
<p>The <b>left_tbl</b> passed to the join criteria generator function will always be the most recently used table -- either the most recently joined table or, if there is none, the main table in the statement. If you want to perform a "chain" of joins, where each table joins from the previous one, you can call <tt>.join()</tt> multiple times, but if you want to join from one table directly to a number of related tables, you can call <tt>.join()</tt> once and pass the table names in as separate arguments:</p>
<pre>
select().from('psn').join('addr').join('zip');
// SELECT * FROM person psn
// INNER JOIN address addr ON psn.addr_id = addr.id
// INNER JOIN zipcode zip ON addr.zip_id = zip.id
select().from('psn').join('addr', 'usr');
// SELECT * FROM person psn
// INNER JOIN address addr ON psn.addr_id = addr.id
// INNER JOIN "user" usr ON psn.usr_id = usr.id
</pre>
<p>If multiple tables are passed to <tt>.join()</tt>, the last one is the most recently used one and it will be used as the basis for the next <tt>.join()</tt>:</p>
<pre>
select().from('psn').join('usr', 'addr').join('zip');
// SELECT * FROM person psn
// INNER JOIN "user" usr ON psn.usr_id = usr.id
// INNER JOIN address addr ON psn.addr_id = addr.id
// INNER JOIN zipcode zip ON addr.zip_id = zip.id
</pre>
<p>Note that this scheme doesn't support complex JOIN table layouts: if you do something like <tt>.join('psn', 'addr').join('zip')</tt> above, it is impossible to also join something to the <tt>'psn'</tt> table. This <i>could</i> be achieved by adding a way to explicitly specify the table you're joining from: <tt>.join('psn', 'addr').join('zip').join('psn->employer')</tt>, but this hasn't been implemented.</p>
</p>
<h2 id="sqlfunctions">SQL Functions</h2>
<p>There are 95 SQL functions defined in SQL-92, including <tt>AVG()</tt>, <tt>COUNT()</tt>, <tt>MIN()</tt>, <tt>MAX()</tt>, <tt>SUM()</tt>, <tt>COALESCE()</tt>, <tt>CASE()</tt>, <tt>LTRIM()</tt>, <tt>RTRIM()</tt>, <tt>UPPER()</tt> and <tt>LOWER()</tt>, among others. These can be easily used in SQL Bricks anywhere that a sql string is expected, such as in a SELECT list:</p>
<pre>
select('COUNT(*)').from('person').where({'access_level': 3});
// SELECT COUNT(*) FROM person WHERE access_level = 3
</pre>
<p>SQL functions can also be used anywhere a value is expected (in the values for an <tt>INSERT</tt> or <tt>UPDATE</tt> or in the right-hand side of a <tt>WHERE</tt> expression) via wrapping a string in the <tt>sql()</tt> function:</p>
<pre>
select().from('person').where({'level_text':
sql("CASE WHEN level=1 THEN 'one' WHEN level=2 THEN 'two' ELSE 'other' END")});
// SELECT * FROM person WHERE level_text =
// CASE WHEN level=1 THEN 'one' WHEN level=2 THEN 'two' ELSE 'other' END
</pre>
<p>Note that column names inside SQL functions that collide with SQL keywords will not be automatically escaped -- you have to do it manually, like this:</p>
<pre>
select('COUNT("order")').from('person');
// SELECT COUNT("order") FROM person
</pre>
<h2 id="askeyword">AS Keyword</h2>
<p>The <tt>AS</tt> keyword is treated like SQL functions. It can be included in column strings and Sql-Bricks will parse it and handle it properly:
<pre>
select('person.name AS personname').from('person');
// SELECT person.name AS personname FROM person
</pre>
<p>This doesn't break automatic column quoting of column names that are reserved words or are capitalized:
<pre>
select('person.order AS person_order').from('person');
// SELECT person."order" AS person_order FROM person
</pre>
<p>That said, if you do anything fancier (SQL functions, etc) then you will need to quote any column names that need quoting manually -- SQL-Bricks only provides the auto-quoting convenience for the most common cases.</p>
<h2 id="contributing">Contributing</h2>
<p>Before sending a pull request, please verify that <a href="browser-tests.html">all the existing tests</a> pass and add new tests for the changes you are making. The tests can be run in node with <tt>npm test</tt> (provided <tt>npm install</tt> has been run to install the dependencies) or they can be run in the browser with <tt>browser-tests.html</tt>. All of the examples in the documentation are run as tests, in addition to the tests in tests.js.</p>
<p>Note that <b>pull requests for additional SQL dialects</b> or extensions beyond ANSI SQL-92 will probably not be merged. If you would like support for a different dialect, you are welcome to maintain a dialect-specific fork or a library that extends sql-bricks.</p>
<p>Also, <b>pull requests for additional SQL statements</b> beyond the four CRUD statements (<tt>SELECT</tt>, <tt>UPDATE</tt>, <tt>INSERT</tt>, <tt>DELETE</tt>) will probably not be merged. Other SQL statements do not benefit as much from re-use and composition; the goal being to keep SQL Bricks small, sharp and low-maintenance.</p>
<h2 id="acknowledgments">Acknowledgments</h2>
<p>Thanks to <a href="https://github.com/Suor">Suor</a> for taking over maintenance of the postgres-dialect extension library: <a href="https://github.com/Suor/sql-bricks-postgres">sql-bricks-postgres</a>. Thanks to <a href="https://github.com/tamarzil">tamarzil</a> for creating and maintaining the mysql-dialect extension library: <a href="https://github.com/tamarzil/mysql-bricks">mysql-bricks</a></p>
<p>Huge thanks to <a href="https://github.com/brianc">Brian C</a> for his hard work supporting and maintaining <a href="https://github.com/brianc/node-sql">node-sql</a> and <a href="https://github.com/brianc/node-postgres">node-postgres</a> and especially for his patience with me and my pull requests.</p>
<p>Also, thanks to <a href="https://github.com/jashkenas">jashkenas</a> for Underscore.js and its documentation, which served as a model and template for these docs.</p>
<h2 id="license">License</h2>
<p>SQL Bricks is <a href="https://github.com/CSNW/sql-bricks/raw/master/LICENSE.md">MIT licensed</a>.</p>
</div>
</div>
<!-- include it so you can play in the console -->
<script type="text/javascript" src="sql-bricks.js"></script>
<script>
sql = SqlBricks;
select = sql.select;
insert = sql.insert;
update = sql.update;
del = sql.delete;
and = sql.and;
or = sql.or;
$in = sql.in;
like = sql.like;
</script>
</body>
</html>