foxhound 2.0.19 → 2.0.21
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 +3 -2
- package/docs/README.md +32 -11
- package/docs/_cover.md +3 -2
- package/docs/_sidebar.md +21 -1
- package/docs/_topbar.md +7 -0
- package/docs/api/README.md +73 -0
- package/docs/api/addFilter.md +170 -0
- package/docs/api/addJoin.md +128 -0
- package/docs/api/addRecord.md +108 -0
- package/docs/api/addSort.md +109 -0
- package/docs/api/behaviorFlags.md +123 -0
- package/docs/api/buildQuery.md +146 -0
- package/docs/api/clone.md +115 -0
- package/docs/api/queryOverrides.md +82 -0
- package/docs/api/setCap.md +115 -0
- package/docs/api/setDataElements.md +95 -0
- package/docs/api/setDialect.md +78 -0
- package/docs/api/setDistinct.md +76 -0
- package/docs/api/setIDUser.md +80 -0
- package/docs/api/setScope.md +70 -0
- package/docs/architecture.md +134 -42
- package/docs/dialects/README.md +2 -0
- package/docs/dialects/postgresql.md +126 -0
- package/docs/quickstart.md +196 -0
- package/docs/retold-catalog.json +40 -1
- package/docs/retold-keyword-index.json +7996 -2630
- package/package.json +2 -2
- package/source/Foxhound-Dialects.js +1 -1
- package/source/dialects/PostgreSQL/FoxHound-Dialect-PostgreSQL.js +17 -17
- package/test/FoxHound-Dialect-PostgreSQL_tests.js +11 -11
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# PostgreSQL Dialect
|
|
2
|
+
|
|
3
|
+
The PostgreSQL dialect generates SQL compatible with PostgreSQL 9.5 and later.
|
|
4
|
+
|
|
5
|
+
## Identifier Quoting
|
|
6
|
+
|
|
7
|
+
PostgreSQL uses double-quote quoting for identifiers, which preserves case sensitivity. Without double quotes, PostgreSQL folds all identifiers to lowercase. FoxHound quotes all column and table names to ensure case-sensitive operation:
|
|
8
|
+
|
|
9
|
+
```sql
|
|
10
|
+
SELECT "Title", "Author" FROM "Books" WHERE "Genre" = :Genre_w0;
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Table-qualified field names are quoted on each segment:
|
|
14
|
+
|
|
15
|
+
```sql
|
|
16
|
+
"Books"."Title"
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Named Parameters
|
|
20
|
+
|
|
21
|
+
The PostgreSQL dialect uses `:name` syntax for named parameters:
|
|
22
|
+
|
|
23
|
+
```sql
|
|
24
|
+
INSERT INTO "Books" ( "Title", "Author") VALUES ( :Title_0, :Author_1);
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The bound values are stored in `query.parameters` as a plain object:
|
|
28
|
+
|
|
29
|
+
```javascript
|
|
30
|
+
{ Title_0: 'Dune', Author_1: 'Frank Herbert' }
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Timestamp Function
|
|
34
|
+
|
|
35
|
+
PostgreSQL uses `NOW()` for timestamp generation. Unlike MySQL's `NOW(3)`, PostgreSQL's `NOW()` natively provides microsecond precision.
|
|
36
|
+
|
|
37
|
+
## Pagination
|
|
38
|
+
|
|
39
|
+
PostgreSQL uses `LIMIT ... OFFSET` syntax:
|
|
40
|
+
|
|
41
|
+
```sql
|
|
42
|
+
-- Cap only
|
|
43
|
+
SELECT "Books".* FROM "Books" LIMIT 20;
|
|
44
|
+
|
|
45
|
+
-- Cap with offset
|
|
46
|
+
SELECT "Books".* FROM "Books" LIMIT 20 OFFSET 40;
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## AutoIdentity on INSERT
|
|
50
|
+
|
|
51
|
+
The PostgreSQL dialect uses `DEFAULT` for AutoIdentity columns on INSERT, allowing PostgreSQL `SERIAL` or `IDENTITY` columns to auto-assign values:
|
|
52
|
+
|
|
53
|
+
```sql
|
|
54
|
+
INSERT INTO "Books" ( "IDBook", "Title") VALUES ( DEFAULT, :Title_1) RETURNING *;
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## RETURNING Clause
|
|
58
|
+
|
|
59
|
+
All INSERT statements include `RETURNING *`, which returns the full inserted row — including any auto-generated values like serial IDs and default timestamps.
|
|
60
|
+
|
|
61
|
+
## Joins
|
|
62
|
+
|
|
63
|
+
```sql
|
|
64
|
+
INNER JOIN Authors ON Authors.IDAuthor = Books.IDAuthor
|
|
65
|
+
LEFT JOIN Publishers ON Publishers.IDPublisher = Books.IDPublisher
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## DISTINCT
|
|
69
|
+
|
|
70
|
+
```sql
|
|
71
|
+
SELECT DISTINCT "Genre" FROM "Books";
|
|
72
|
+
SELECT COUNT(DISTINCT "Books"."IDBook") AS RowCount FROM "Books";
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Case Sensitivity
|
|
76
|
+
|
|
77
|
+
PostgreSQL treats unquoted identifiers as case-insensitive (folded to lowercase). The FoxHound PostgreSQL dialect wraps all column and table names in double quotes to preserve the exact case of your schema definitions. This is important when your schema uses mixed-case column names like `IDBook`, `CreateDate`, or `UpdatingIDUser`.
|
|
78
|
+
|
|
79
|
+
If your PostgreSQL tables were created with quoted identifiers:
|
|
80
|
+
|
|
81
|
+
```sql
|
|
82
|
+
CREATE TABLE "Books" (
|
|
83
|
+
"IDBook" SERIAL PRIMARY KEY,
|
|
84
|
+
"Title" VARCHAR(255),
|
|
85
|
+
"PublishedYear" INTEGER
|
|
86
|
+
);
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Then FoxHound's quoted queries will match these column names correctly.
|
|
90
|
+
|
|
91
|
+
## Soft Delete
|
|
92
|
+
|
|
93
|
+
When a schema has a `Deleted` column type, the Delete operation generates an UPDATE:
|
|
94
|
+
|
|
95
|
+
```sql
|
|
96
|
+
UPDATE "Books" SET "Deleted" = 1, "DeleteDate" = NOW(),
|
|
97
|
+
"UpdateDate" = NOW(), "DeleteIDUser" = :DeleteIDUser_3
|
|
98
|
+
WHERE "IDBook" = :IDBook_w0;
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Query Overrides
|
|
102
|
+
|
|
103
|
+
The PostgreSQL dialect supports underscore-style templates for Read and Count queries:
|
|
104
|
+
|
|
105
|
+
| Variable | Description |
|
|
106
|
+
|----------|-------------|
|
|
107
|
+
| `<%= FieldList %>` | Generated column list with double-quote quoting |
|
|
108
|
+
| `<%= TableName %>` | Table name with double-quote quoting |
|
|
109
|
+
| `<%= Where %>` | WHERE clause (including the keyword) |
|
|
110
|
+
| `<%= Join %>` | JOIN clauses |
|
|
111
|
+
| `<%= OrderBy %>` | ORDER BY clause |
|
|
112
|
+
| `<%= Limit %>` | LIMIT/OFFSET clause |
|
|
113
|
+
| `<%= Distinct %>` | DISTINCT keyword (if set) |
|
|
114
|
+
| `<%= _Params %>` | Full parameters object |
|
|
115
|
+
|
|
116
|
+
## Comparison with Other Dialects
|
|
117
|
+
|
|
118
|
+
| Feature | PostgreSQL | MySQL | MSSQL |
|
|
119
|
+
|---------|-----------|-------|-------|
|
|
120
|
+
| Identifier quoting | `"double quotes"` | `` `backticks` `` | `[brackets]` |
|
|
121
|
+
| Parameter prefix | `:name` | `:name` | `@name` |
|
|
122
|
+
| Timestamp function | `NOW()` | `NOW(3)` | `GETUTCDATE()` |
|
|
123
|
+
| AutoIdentity INSERT | `DEFAULT` | `NULL` | column omitted |
|
|
124
|
+
| INSERT return | `RETURNING *` | none | none |
|
|
125
|
+
| Pagination | `LIMIT n OFFSET m` | `LIMIT m, n` | `OFFSET m ROWS FETCH NEXT n ROWS ONLY` |
|
|
126
|
+
| Case sensitivity | case-sensitive (quoted) | case-insensitive | case-insensitive |
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# Quickstart
|
|
2
|
+
|
|
3
|
+
Get up and running with FoxHound in under five minutes.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install foxhound fable
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
FoxHound requires [Fable](https://github.com/stevenvelozo/fable) as a peer dependency for logging, UUID generation, and utility functions.
|
|
12
|
+
|
|
13
|
+
## Your First Query
|
|
14
|
+
|
|
15
|
+
```javascript
|
|
16
|
+
var libFable = require('fable');
|
|
17
|
+
var libFoxHound = require('foxhound');
|
|
18
|
+
|
|
19
|
+
// Create a Fable instance (provides logging, UUIDs, and utilities)
|
|
20
|
+
var _Fable = new libFable({Product: 'MyApp'});
|
|
21
|
+
|
|
22
|
+
// Create a new FoxHound query
|
|
23
|
+
var tmpQuery = libFoxHound.new(_Fable);
|
|
24
|
+
|
|
25
|
+
// Build a simple SELECT
|
|
26
|
+
tmpQuery
|
|
27
|
+
.setScope('Books')
|
|
28
|
+
.setDialect('MySQL')
|
|
29
|
+
.buildReadQuery();
|
|
30
|
+
|
|
31
|
+
console.log(tmpQuery.query.body);
|
|
32
|
+
// => SELECT `Books`.* FROM `Books`;
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Selecting Specific Columns
|
|
36
|
+
|
|
37
|
+
```javascript
|
|
38
|
+
var tmpQuery = libFoxHound.new(_Fable)
|
|
39
|
+
.setDialect('MySQL')
|
|
40
|
+
.setScope('Books')
|
|
41
|
+
.setDataElements(['Title', 'Author', 'PublishedYear'])
|
|
42
|
+
.buildReadQuery();
|
|
43
|
+
|
|
44
|
+
console.log(tmpQuery.query.body);
|
|
45
|
+
// => SELECT `Title`, `Author`, `PublishedYear` FROM `Books`;
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Adding Filters
|
|
49
|
+
|
|
50
|
+
```javascript
|
|
51
|
+
var tmpQuery = libFoxHound.new(_Fable)
|
|
52
|
+
.setDialect('MySQL')
|
|
53
|
+
.setScope('Books')
|
|
54
|
+
.setDataElements(['Title', 'Author'])
|
|
55
|
+
.addFilter('Genre', 'Science Fiction')
|
|
56
|
+
.addFilter('PublishedYear', 2000, '>')
|
|
57
|
+
.buildReadQuery();
|
|
58
|
+
|
|
59
|
+
console.log(tmpQuery.query.body);
|
|
60
|
+
// => SELECT `Title`, `Author` FROM `Books`
|
|
61
|
+
// WHERE `Books`.`Genre` = :Genre_w0
|
|
62
|
+
// AND `Books`.`PublishedYear` > :PublishedYear_w1;
|
|
63
|
+
|
|
64
|
+
console.log(tmpQuery.query.parameters);
|
|
65
|
+
// => { Genre_w0: 'Science Fiction', PublishedYear_w1: 2000 }
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Sorting and Pagination
|
|
69
|
+
|
|
70
|
+
```javascript
|
|
71
|
+
var tmpQuery = libFoxHound.new(_Fable)
|
|
72
|
+
.setDialect('MySQL')
|
|
73
|
+
.setScope('Books')
|
|
74
|
+
.addSort({Column: 'PublishedYear', Direction: 'Descending'})
|
|
75
|
+
.setCap(25)
|
|
76
|
+
.setBegin(0)
|
|
77
|
+
.buildReadQuery();
|
|
78
|
+
|
|
79
|
+
console.log(tmpQuery.query.body);
|
|
80
|
+
// => SELECT `Books`.* FROM `Books`
|
|
81
|
+
// ORDER BY PublishedYear DESC LIMIT 0, 25;
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Creating a Record
|
|
85
|
+
|
|
86
|
+
```javascript
|
|
87
|
+
var tmpQuery = libFoxHound.new(_Fable)
|
|
88
|
+
.setDialect('MySQL')
|
|
89
|
+
.setScope('Books')
|
|
90
|
+
.addRecord({Title: 'Dune', Author: 'Frank Herbert', PublishedYear: 1965})
|
|
91
|
+
.buildCreateQuery();
|
|
92
|
+
|
|
93
|
+
console.log(tmpQuery.query.body);
|
|
94
|
+
// => INSERT INTO `Books` ( Title, Author, PublishedYear)
|
|
95
|
+
// VALUES ( :Title_0, :Author_1, :PublishedYear_2);
|
|
96
|
+
|
|
97
|
+
console.log(tmpQuery.query.parameters);
|
|
98
|
+
// => { Title_0: 'Dune', Author_1: 'Frank Herbert', PublishedYear_2: 1965 }
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Updating a Record
|
|
102
|
+
|
|
103
|
+
```javascript
|
|
104
|
+
var tmpQuery = libFoxHound.new(_Fable)
|
|
105
|
+
.setDialect('MySQL')
|
|
106
|
+
.setScope('Books')
|
|
107
|
+
.addFilter('IDBook', 42)
|
|
108
|
+
.addRecord({Title: 'Dune Messiah', PublishedYear: 1969})
|
|
109
|
+
.buildUpdateQuery();
|
|
110
|
+
|
|
111
|
+
console.log(tmpQuery.query.body);
|
|
112
|
+
// => UPDATE `Books` SET Title = :Title_0, PublishedYear = :PublishedYear_1
|
|
113
|
+
// WHERE IDBook = :IDBook_w0;
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Deleting a Record
|
|
117
|
+
|
|
118
|
+
```javascript
|
|
119
|
+
var tmpQuery = libFoxHound.new(_Fable)
|
|
120
|
+
.setDialect('MySQL')
|
|
121
|
+
.setScope('Books')
|
|
122
|
+
.addFilter('IDBook', 42)
|
|
123
|
+
.buildDeleteQuery();
|
|
124
|
+
|
|
125
|
+
console.log(tmpQuery.query.body);
|
|
126
|
+
// => DELETE FROM `Books` WHERE IDBook = :IDBook_w0;
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Switching Dialects
|
|
130
|
+
|
|
131
|
+
The same query configuration works across all dialects:
|
|
132
|
+
|
|
133
|
+
```javascript
|
|
134
|
+
var tmpQuery = libFoxHound.new(_Fable)
|
|
135
|
+
.setScope('Books')
|
|
136
|
+
.setDataElements(['Title', 'Author'])
|
|
137
|
+
.addFilter('Genre', 'Fantasy')
|
|
138
|
+
.setCap(10);
|
|
139
|
+
|
|
140
|
+
// MySQL
|
|
141
|
+
tmpQuery.setDialect('MySQL').buildReadQuery();
|
|
142
|
+
console.log(tmpQuery.query.body);
|
|
143
|
+
// => SELECT `Title`, `Author` FROM `Books`
|
|
144
|
+
// WHERE `Books`.`Genre` = :Genre_w0 LIMIT 10;
|
|
145
|
+
|
|
146
|
+
// PostgreSQL
|
|
147
|
+
tmpQuery.setDialect('PostgreSQL').buildReadQuery();
|
|
148
|
+
console.log(tmpQuery.query.body);
|
|
149
|
+
// => SELECT "Title", "Author" FROM "Books"
|
|
150
|
+
// WHERE "Genre" = :Genre_w0 LIMIT 10;
|
|
151
|
+
|
|
152
|
+
// MSSQL
|
|
153
|
+
tmpQuery.setDialect('MSSQL').buildReadQuery();
|
|
154
|
+
console.log(tmpQuery.query.body);
|
|
155
|
+
// => SELECT [Title], [Author] FROM [Books]
|
|
156
|
+
// WHERE Genre = @Genre_w0 OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY;
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Using with a Schema
|
|
160
|
+
|
|
161
|
+
Attaching a schema enables automatic management of identity, timestamp, and soft-delete columns:
|
|
162
|
+
|
|
163
|
+
```javascript
|
|
164
|
+
var tmpQuery = libFoxHound.new(_Fable)
|
|
165
|
+
.setDialect('MySQL')
|
|
166
|
+
.setScope('Books')
|
|
167
|
+
.addRecord({IDBook: null, Title: 'Neuromancer', Author: 'William Gibson'});
|
|
168
|
+
|
|
169
|
+
tmpQuery.query.schema = [
|
|
170
|
+
{Column: 'IDBook', Type: 'AutoIdentity'},
|
|
171
|
+
{Column: 'Title', Type: 'String'},
|
|
172
|
+
{Column: 'Author', Type: 'String'},
|
|
173
|
+
{Column: 'CreateDate', Type: 'CreateDate'},
|
|
174
|
+
{Column: 'CreatingIDUser', Type: 'CreateIDUser'},
|
|
175
|
+
{Column: 'UpdateDate', Type: 'UpdateDate'},
|
|
176
|
+
{Column: 'UpdatingIDUser', Type: 'UpdateIDUser'},
|
|
177
|
+
{Column: 'Deleted', Type: 'Deleted'},
|
|
178
|
+
{Column: 'DeleteDate', Type: 'DeleteDate'},
|
|
179
|
+
{Column: 'DeletingIDUser', Type: 'DeleteIDUser'}
|
|
180
|
+
];
|
|
181
|
+
tmpQuery.query.IDUser = 5;
|
|
182
|
+
|
|
183
|
+
tmpQuery.buildCreateQuery();
|
|
184
|
+
|
|
185
|
+
console.log(tmpQuery.query.body);
|
|
186
|
+
// => INSERT INTO `Books` ( IDBook, Title, Author, CreateDate, ...)
|
|
187
|
+
// VALUES ( NULL, :Title_1, :Author_2, NOW(3), ...);
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Next Steps
|
|
191
|
+
|
|
192
|
+
- [Architecture](architecture.md) — understand FoxHound's internal design
|
|
193
|
+
- [Filters](filters.md) — learn about filter operators and grouping
|
|
194
|
+
- [Schema Integration](schema.md) — use schemas for automatic column management
|
|
195
|
+
- [Dialects](dialects/README.md) — explore dialect-specific features
|
|
196
|
+
- [API Reference](api/README.md) — complete function reference
|
package/docs/retold-catalog.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"Generated": "2026-
|
|
2
|
+
"Generated": "2026-03-03T14:33:09.873Z",
|
|
3
3
|
"GitHubOrg": "stevenvelozo",
|
|
4
4
|
"DefaultBranch": "master",
|
|
5
5
|
"Groups": [
|
|
@@ -69,6 +69,44 @@
|
|
|
69
69
|
"Key": "docs",
|
|
70
70
|
"Description": "",
|
|
71
71
|
"Modules": [
|
|
72
|
+
{
|
|
73
|
+
"Name": "api",
|
|
74
|
+
"Repo": "api",
|
|
75
|
+
"Group": "docs",
|
|
76
|
+
"Branch": "master",
|
|
77
|
+
"HasDocs": true,
|
|
78
|
+
"HasCover": false,
|
|
79
|
+
"Sidebar": [],
|
|
80
|
+
"DocFiles": [
|
|
81
|
+
"api/README.md",
|
|
82
|
+
"api/addFilter.md",
|
|
83
|
+
"api/addJoin.md",
|
|
84
|
+
"api/addRecord.md",
|
|
85
|
+
"api/addSort.md",
|
|
86
|
+
"api/behaviorFlags.md",
|
|
87
|
+
"api/buildQuery.md",
|
|
88
|
+
"api/clone.md",
|
|
89
|
+
"api/queryOverrides.md",
|
|
90
|
+
"api/setCap.md",
|
|
91
|
+
"api/setDataElements.md",
|
|
92
|
+
"api/setDialect.md",
|
|
93
|
+
"api/setDistinct.md",
|
|
94
|
+
"api/setIDUser.md",
|
|
95
|
+
"api/setScope.md"
|
|
96
|
+
]
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"Name": "css",
|
|
100
|
+
"Repo": "css",
|
|
101
|
+
"Group": "docs",
|
|
102
|
+
"Branch": "master",
|
|
103
|
+
"HasDocs": true,
|
|
104
|
+
"HasCover": false,
|
|
105
|
+
"Sidebar": [],
|
|
106
|
+
"DocFiles": [
|
|
107
|
+
"css/docuserve.css"
|
|
108
|
+
]
|
|
109
|
+
},
|
|
72
110
|
{
|
|
73
111
|
"Name": "dialects",
|
|
74
112
|
"Repo": "dialects",
|
|
@@ -82,6 +120,7 @@
|
|
|
82
120
|
"dialects/alasql.md",
|
|
83
121
|
"dialects/mssql.md",
|
|
84
122
|
"dialects/mysql.md",
|
|
123
|
+
"dialects/postgresql.md",
|
|
85
124
|
"dialects/sqlite.md"
|
|
86
125
|
]
|
|
87
126
|
},
|