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,95 @@
|
|
|
1
|
+
# setDataElements
|
|
2
|
+
|
|
3
|
+
Set which columns to include in the SELECT clause.
|
|
4
|
+
|
|
5
|
+
## Signature
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
tmpQuery.setDataElements(pDataElements)
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Parameters
|
|
12
|
+
|
|
13
|
+
| Parameter | Type | Required | Description |
|
|
14
|
+
|-----------|------|----------|-------------|
|
|
15
|
+
| `pDataElements` | Array or String | Yes | Column name(s) to select |
|
|
16
|
+
|
|
17
|
+
## Returns
|
|
18
|
+
|
|
19
|
+
Returns `this` for chaining.
|
|
20
|
+
|
|
21
|
+
## Description
|
|
22
|
+
|
|
23
|
+
Sets the list of columns to include in the SELECT field list. When not set (or set to `false`), the query selects all columns using `*`.
|
|
24
|
+
|
|
25
|
+
If a string is passed, it is automatically wrapped in an array.
|
|
26
|
+
|
|
27
|
+
## Examples
|
|
28
|
+
|
|
29
|
+
### Select all columns (default)
|
|
30
|
+
|
|
31
|
+
```javascript
|
|
32
|
+
tmpQuery.setScope('Books').setDialect('MySQL').buildReadQuery();
|
|
33
|
+
// => SELECT `Books`.* FROM `Books`;
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Select specific columns
|
|
37
|
+
|
|
38
|
+
```javascript
|
|
39
|
+
tmpQuery
|
|
40
|
+
.setScope('Books')
|
|
41
|
+
.setDataElements(['Title', 'Author', 'PublishedYear'])
|
|
42
|
+
.setDialect('MySQL')
|
|
43
|
+
.buildReadQuery();
|
|
44
|
+
|
|
45
|
+
// => SELECT `Title`, `Author`, `PublishedYear` FROM `Books`;
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Single column as string
|
|
49
|
+
|
|
50
|
+
```javascript
|
|
51
|
+
tmpQuery.setDataElements('Title');
|
|
52
|
+
// Automatically converted to ['Title']
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Column aliases
|
|
56
|
+
|
|
57
|
+
Pass an array pair `[column, alias]` to create a column alias:
|
|
58
|
+
|
|
59
|
+
```javascript
|
|
60
|
+
tmpQuery
|
|
61
|
+
.setScope('Books')
|
|
62
|
+
.setDataElements([
|
|
63
|
+
['Books.Title', 'BookTitle'],
|
|
64
|
+
['Books.Author', 'AuthorName']
|
|
65
|
+
])
|
|
66
|
+
.setDialect('MySQL')
|
|
67
|
+
.buildReadQuery();
|
|
68
|
+
|
|
69
|
+
// => SELECT `Books`.`Title` AS `BookTitle`, `Books`.`Author` AS `AuthorName`
|
|
70
|
+
// FROM `Books`;
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Wildcard with specific columns
|
|
74
|
+
|
|
75
|
+
```javascript
|
|
76
|
+
tmpQuery
|
|
77
|
+
.setScope('Books')
|
|
78
|
+
.setDataElements(['*', 'Authors.Name'])
|
|
79
|
+
.setDialect('PostgreSQL')
|
|
80
|
+
.buildReadQuery();
|
|
81
|
+
|
|
82
|
+
// => SELECT *, "Authors"."Name" FROM "Books";
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Table-qualified wildcard
|
|
86
|
+
|
|
87
|
+
```javascript
|
|
88
|
+
tmpQuery
|
|
89
|
+
.setScope('Books')
|
|
90
|
+
.setDataElements(['Books.*', 'Authors.Name'])
|
|
91
|
+
.setDialect('PostgreSQL')
|
|
92
|
+
.buildReadQuery();
|
|
93
|
+
|
|
94
|
+
// => SELECT "Books".*, "Authors"."Name" FROM "Books";
|
|
95
|
+
```
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# setDialect
|
|
2
|
+
|
|
3
|
+
Set the SQL dialect used for query generation.
|
|
4
|
+
|
|
5
|
+
## Signature
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
tmpQuery.setDialect(pDialectName)
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Parameters
|
|
12
|
+
|
|
13
|
+
| Parameter | Type | Required | Description |
|
|
14
|
+
|-----------|------|----------|-------------|
|
|
15
|
+
| `pDialectName` | String | Yes | Name of the dialect |
|
|
16
|
+
|
|
17
|
+
## Returns
|
|
18
|
+
|
|
19
|
+
Returns `this` for chaining.
|
|
20
|
+
|
|
21
|
+
## Available Dialects
|
|
22
|
+
|
|
23
|
+
| Name | Target Database |
|
|
24
|
+
|------|----------------|
|
|
25
|
+
| `'MySQL'` | MySQL / MariaDB |
|
|
26
|
+
| `'PostgreSQL'` | PostgreSQL 9.5+ |
|
|
27
|
+
| `'MSSQL'` | Microsoft SQL Server |
|
|
28
|
+
| `'SQLite'` | SQLite 3 |
|
|
29
|
+
| `'ALASQL'` | ALASQL (in-memory JavaScript) |
|
|
30
|
+
| `'English'` | Human-readable text (default) |
|
|
31
|
+
| `'MeadowEndpoints'` | REST URL generation |
|
|
32
|
+
| `'MongoDB'` | MongoDB query objects |
|
|
33
|
+
| `'DGraph'` | DGraph graph queries |
|
|
34
|
+
| `'Solr'` | Apache Solr search |
|
|
35
|
+
|
|
36
|
+
## Description
|
|
37
|
+
|
|
38
|
+
The dialect determines how FoxHound translates the internal query parameters into database-specific syntax. This includes identifier quoting, parameter prefixes, pagination syntax, timestamp functions, and auto-identity handling.
|
|
39
|
+
|
|
40
|
+
If an invalid dialect name is passed, FoxHound logs an error and falls back to the `English` dialect.
|
|
41
|
+
|
|
42
|
+
## Examples
|
|
43
|
+
|
|
44
|
+
### Basic usage
|
|
45
|
+
|
|
46
|
+
```javascript
|
|
47
|
+
tmpQuery.setDialect('MySQL');
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Same query, different dialects
|
|
51
|
+
|
|
52
|
+
```javascript
|
|
53
|
+
var tmpQuery = libFoxHound.new(_Fable)
|
|
54
|
+
.setScope('Books')
|
|
55
|
+
.addFilter('Genre', 'Fantasy')
|
|
56
|
+
.setCap(10);
|
|
57
|
+
|
|
58
|
+
tmpQuery.setDialect('MySQL').buildReadQuery();
|
|
59
|
+
console.log(tmpQuery.query.body);
|
|
60
|
+
// => SELECT `Books`.* FROM `Books` WHERE `Books`.`Genre` = :Genre_w0 LIMIT 10;
|
|
61
|
+
|
|
62
|
+
tmpQuery.setDialect('PostgreSQL').buildReadQuery();
|
|
63
|
+
console.log(tmpQuery.query.body);
|
|
64
|
+
// => SELECT "Books".* FROM "Books" WHERE "Genre" = :Genre_w0 LIMIT 10;
|
|
65
|
+
|
|
66
|
+
tmpQuery.setDialect('MSSQL').buildReadQuery();
|
|
67
|
+
console.log(tmpQuery.query.body);
|
|
68
|
+
// => SELECT [Books].* FROM [Books] WHERE Genre = @Genre_w0
|
|
69
|
+
// OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY;
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Checking the active dialect
|
|
73
|
+
|
|
74
|
+
```javascript
|
|
75
|
+
tmpQuery.setDialect('PostgreSQL');
|
|
76
|
+
console.log(tmpQuery.dialect.name);
|
|
77
|
+
// => 'PostgreSQL'
|
|
78
|
+
```
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# setDistinct
|
|
2
|
+
|
|
3
|
+
Enable or disable DISTINCT result filtering.
|
|
4
|
+
|
|
5
|
+
## Signature
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
tmpQuery.setDistinct(pDistinct)
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Parameters
|
|
12
|
+
|
|
13
|
+
| Parameter | Type | Required | Description |
|
|
14
|
+
|-----------|------|----------|-------------|
|
|
15
|
+
| `pDistinct` | Boolean | Yes | `true` to enable DISTINCT |
|
|
16
|
+
|
|
17
|
+
## Returns
|
|
18
|
+
|
|
19
|
+
Returns `this` for chaining.
|
|
20
|
+
|
|
21
|
+
## Description
|
|
22
|
+
|
|
23
|
+
When enabled, the generated SELECT statement includes the `DISTINCT` keyword to return only unique rows.
|
|
24
|
+
|
|
25
|
+
For Count queries with DISTINCT, FoxHound counts distinct values of the selected field list (or the AutoIdentity column from the schema if no specific fields are set).
|
|
26
|
+
|
|
27
|
+
## Examples
|
|
28
|
+
|
|
29
|
+
### DISTINCT select
|
|
30
|
+
|
|
31
|
+
```javascript
|
|
32
|
+
tmpQuery
|
|
33
|
+
.setScope('Books')
|
|
34
|
+
.setDataElements(['Genre'])
|
|
35
|
+
.setDistinct(true)
|
|
36
|
+
.setDialect('MySQL')
|
|
37
|
+
.buildReadQuery();
|
|
38
|
+
|
|
39
|
+
// => SELECT DISTINCT `Genre` FROM `Books`;
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### DISTINCT count
|
|
43
|
+
|
|
44
|
+
```javascript
|
|
45
|
+
tmpQuery
|
|
46
|
+
.setScope('Books')
|
|
47
|
+
.setDataElements(['Author'])
|
|
48
|
+
.setDistinct(true)
|
|
49
|
+
.setDialect('MySQL')
|
|
50
|
+
.buildCountQuery();
|
|
51
|
+
|
|
52
|
+
// => SELECT COUNT(DISTINCT `Author`) AS RowCount FROM `Books`;
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### DISTINCT count with schema (no explicit fields)
|
|
56
|
+
|
|
57
|
+
```javascript
|
|
58
|
+
tmpQuery
|
|
59
|
+
.setScope('Books')
|
|
60
|
+
.setDistinct(true);
|
|
61
|
+
|
|
62
|
+
tmpQuery.query.schema = [
|
|
63
|
+
{Column: 'IDBook', Type: 'AutoIdentity'},
|
|
64
|
+
{Column: 'Title', Type: 'String'}
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
tmpQuery.setDialect('MySQL').buildCountQuery();
|
|
68
|
+
|
|
69
|
+
// => SELECT COUNT(DISTINCT `Books`.`IDBook`) AS RowCount FROM `Books`;
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Disable DISTINCT
|
|
73
|
+
|
|
74
|
+
```javascript
|
|
75
|
+
tmpQuery.setDistinct(false);
|
|
76
|
+
```
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# setIDUser
|
|
2
|
+
|
|
3
|
+
Set the user ID for automatic audit column stamping.
|
|
4
|
+
|
|
5
|
+
## Signature
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
tmpQuery.setIDUser(pIDUser)
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Parameters
|
|
12
|
+
|
|
13
|
+
| Parameter | Type | Required | Description |
|
|
14
|
+
|-----------|------|----------|-------------|
|
|
15
|
+
| `pIDUser` | Integer | Yes | User ID (must be >= 0) |
|
|
16
|
+
|
|
17
|
+
## Returns
|
|
18
|
+
|
|
19
|
+
Returns `this` for chaining.
|
|
20
|
+
|
|
21
|
+
## Description
|
|
22
|
+
|
|
23
|
+
Sets the user ID that FoxHound uses when auto-populating audit columns in schema-aware queries. This value is applied to:
|
|
24
|
+
|
|
25
|
+
- `CreateIDUser` columns on INSERT
|
|
26
|
+
- `UpdateIDUser` columns on INSERT and UPDATE
|
|
27
|
+
- `DeleteIDUser` columns on soft DELETE
|
|
28
|
+
|
|
29
|
+
The value defaults to `0` if not set.
|
|
30
|
+
|
|
31
|
+
## Examples
|
|
32
|
+
|
|
33
|
+
### Basic usage
|
|
34
|
+
|
|
35
|
+
```javascript
|
|
36
|
+
tmpQuery
|
|
37
|
+
.setScope('Books')
|
|
38
|
+
.addRecord({Title: 'Dune', Author: 'Frank Herbert'})
|
|
39
|
+
.setIDUser(42);
|
|
40
|
+
|
|
41
|
+
tmpQuery.query.schema = [
|
|
42
|
+
{Column: 'IDBook', Type: 'AutoIdentity'},
|
|
43
|
+
{Column: 'Title', Type: 'String'},
|
|
44
|
+
{Column: 'Author', Type: 'String'},
|
|
45
|
+
{Column: 'CreatingIDUser', Type: 'CreateIDUser'},
|
|
46
|
+
{Column: 'UpdatingIDUser', Type: 'UpdateIDUser'}
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
tmpQuery.setDialect('MySQL').buildCreateQuery();
|
|
50
|
+
|
|
51
|
+
// CreatingIDUser => 42
|
|
52
|
+
// UpdatingIDUser => 42
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### With soft delete
|
|
56
|
+
|
|
57
|
+
```javascript
|
|
58
|
+
tmpQuery
|
|
59
|
+
.setScope('Books')
|
|
60
|
+
.addFilter('IDBook', 99)
|
|
61
|
+
.setIDUser(5);
|
|
62
|
+
|
|
63
|
+
tmpQuery.query.schema = [
|
|
64
|
+
{Column: 'IDBook', Type: 'AutoIdentity'},
|
|
65
|
+
{Column: 'Deleted', Type: 'Deleted'},
|
|
66
|
+
{Column: 'DeleteDate', Type: 'DeleteDate'},
|
|
67
|
+
{Column: 'DeletingIDUser', Type: 'DeleteIDUser'},
|
|
68
|
+
{Column: 'UpdateDate', Type: 'UpdateDate'}
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
tmpQuery.setDialect('MySQL').buildDeleteQuery();
|
|
72
|
+
|
|
73
|
+
// DeletingIDUser => 5
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Alternative: direct property access
|
|
77
|
+
|
|
78
|
+
```javascript
|
|
79
|
+
tmpQuery.query.IDUser = 42;
|
|
80
|
+
```
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# setScope
|
|
2
|
+
|
|
3
|
+
Set the primary table or collection for the query.
|
|
4
|
+
|
|
5
|
+
## Signature
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
tmpQuery.setScope(pScope)
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Parameters
|
|
12
|
+
|
|
13
|
+
| Parameter | Type | Required | Description |
|
|
14
|
+
|-----------|------|----------|-------------|
|
|
15
|
+
| `pScope` | String | Yes | Table or collection name |
|
|
16
|
+
|
|
17
|
+
## Returns
|
|
18
|
+
|
|
19
|
+
Returns `this` for chaining.
|
|
20
|
+
|
|
21
|
+
## Description
|
|
22
|
+
|
|
23
|
+
The scope defines which table (or collection, in NoSQL dialects) the query targets. This is required for all query types — it appears as the `FROM` clause in SELECT, `INSERT INTO` in CREATE, `UPDATE` in UPDATE, and `DELETE FROM` in DELETE.
|
|
24
|
+
|
|
25
|
+
FoxHound validates that the input is a string. Non-string values are ignored and an error is logged.
|
|
26
|
+
|
|
27
|
+
## Examples
|
|
28
|
+
|
|
29
|
+
### Basic usage
|
|
30
|
+
|
|
31
|
+
```javascript
|
|
32
|
+
tmpQuery.setScope('Books');
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### With dialect quoting
|
|
36
|
+
|
|
37
|
+
Each dialect quotes the scope name according to its conventions:
|
|
38
|
+
|
|
39
|
+
```javascript
|
|
40
|
+
// MySQL
|
|
41
|
+
tmpQuery.setScope('Books').setDialect('MySQL').buildReadQuery();
|
|
42
|
+
// => SELECT `Books`.* FROM `Books`;
|
|
43
|
+
|
|
44
|
+
// PostgreSQL
|
|
45
|
+
tmpQuery.setScope('Books').setDialect('PostgreSQL').buildReadQuery();
|
|
46
|
+
// => SELECT "Books".* FROM "Books";
|
|
47
|
+
|
|
48
|
+
// MSSQL
|
|
49
|
+
tmpQuery.setScope('Books').setDialect('MSSQL').buildReadQuery();
|
|
50
|
+
// => SELECT [Books].* FROM [Books];
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Pre-quoted scope
|
|
54
|
+
|
|
55
|
+
If the scope is already quoted for your dialect, FoxHound preserves it:
|
|
56
|
+
|
|
57
|
+
```javascript
|
|
58
|
+
tmpQuery.setScope('"MySchema"."Books"');
|
|
59
|
+
// PostgreSQL will use: "MySchema"."Books"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Chaining
|
|
63
|
+
|
|
64
|
+
```javascript
|
|
65
|
+
tmpQuery
|
|
66
|
+
.setScope('Books')
|
|
67
|
+
.setDataElements(['Title', 'Author'])
|
|
68
|
+
.setDialect('MySQL')
|
|
69
|
+
.buildReadQuery();
|
|
70
|
+
```
|
package/docs/architecture.md
CHANGED
|
@@ -4,36 +4,76 @@ FoxHound follows a clean separation between query configuration, dialect-specifi
|
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
7
|
+
```mermaid
|
|
8
|
+
graph TD
|
|
9
|
+
A[Application Code] -->|configure| B[FoxHound Core]
|
|
10
|
+
B -->|setDialect| C{Dialect Engine}
|
|
11
|
+
C -->|MySQL| D[MySQL Generator]
|
|
12
|
+
C -->|PostgreSQL| E[PostgreSQL Generator]
|
|
13
|
+
C -->|MSSQL| F[MSSQL Generator]
|
|
14
|
+
C -->|SQLite| G[SQLite Generator]
|
|
15
|
+
C -->|ALASQL| H[ALASQL Generator]
|
|
16
|
+
C -->|English| I[English Generator]
|
|
17
|
+
D --> J[Query Output]
|
|
18
|
+
E --> J
|
|
19
|
+
F --> J
|
|
20
|
+
G --> J
|
|
21
|
+
H --> J
|
|
22
|
+
I --> J
|
|
23
|
+
J -->|query.body| K[SQL String]
|
|
24
|
+
J -->|query.parameters| L[Bound Values]
|
|
7
25
|
```
|
|
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
|
-
|
|
26
|
+
|
|
27
|
+
## Query Lifecycle
|
|
28
|
+
|
|
29
|
+
```mermaid
|
|
30
|
+
sequenceDiagram
|
|
31
|
+
participant App as Application
|
|
32
|
+
participant FH as FoxHound
|
|
33
|
+
participant D as Dialect
|
|
34
|
+
participant P as Parameters
|
|
35
|
+
|
|
36
|
+
App->>FH: libFoxHound.new(_Fable)
|
|
37
|
+
FH->>P: Initialize Parameters
|
|
38
|
+
App->>FH: setScope('Books')
|
|
39
|
+
App->>FH: addFilter('Genre', 'Sci-Fi')
|
|
40
|
+
App->>FH: setDialect('MySQL')
|
|
41
|
+
App->>FH: buildReadQuery()
|
|
42
|
+
FH->>D: Read(parameters)
|
|
43
|
+
D->>D: generateFieldList()
|
|
44
|
+
D->>D: generateWhere()
|
|
45
|
+
D->>D: generateOrderBy()
|
|
46
|
+
D->>D: generateLimit()
|
|
47
|
+
D-->>FH: SQL string
|
|
48
|
+
FH->>P: Store in query.body
|
|
49
|
+
App->>FH: query.body
|
|
50
|
+
FH-->>App: SELECT `Books`.* FROM `Books` WHERE ...
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Component Architecture
|
|
54
|
+
|
|
55
|
+
```mermaid
|
|
56
|
+
graph LR
|
|
57
|
+
subgraph FoxHound Core
|
|
58
|
+
A[FoxHound.js] --> B[Parameters.js]
|
|
59
|
+
A --> C[Foxhound-Dialects.js]
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
subgraph Dialect Modules
|
|
63
|
+
C --> D[MySQL]
|
|
64
|
+
C --> E[PostgreSQL]
|
|
65
|
+
C --> F[MSSQL]
|
|
66
|
+
C --> G[SQLite]
|
|
67
|
+
C --> H[ALASQL]
|
|
68
|
+
C --> I[English]
|
|
69
|
+
C --> J[MeadowEndpoints]
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
subgraph External
|
|
73
|
+
K[Fable] --> A
|
|
74
|
+
L[Meadow] --> A
|
|
75
|
+
M[Stricture] --> B
|
|
76
|
+
end
|
|
37
77
|
```
|
|
38
78
|
|
|
39
79
|
## Factory Pattern
|
|
@@ -70,12 +110,42 @@ All query state lives in a single `Parameters` object:
|
|
|
70
110
|
|
|
71
111
|
Each dialect is a module that exports a factory function accepting a Fable instance. The returned object exposes six methods that each accept a Parameters object and return a SQL string:
|
|
72
112
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
113
|
+
```mermaid
|
|
114
|
+
classDiagram
|
|
115
|
+
class Dialect {
|
|
116
|
+
+Create(pParameters) String
|
|
117
|
+
+Read(pParameters) String
|
|
118
|
+
+Update(pParameters) String
|
|
119
|
+
+Delete(pParameters) String
|
|
120
|
+
+Undelete(pParameters) String
|
|
121
|
+
+Count(pParameters) String
|
|
122
|
+
+name String
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
class MySQL {
|
|
126
|
+
+backtick quoting
|
|
127
|
+
+colon parameters
|
|
128
|
+
+NOW 3 timestamps
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
class PostgreSQL {
|
|
132
|
+
+double-quote quoting
|
|
133
|
+
+colon parameters
|
|
134
|
+
+NOW timestamps
|
|
135
|
+
+RETURNING clause
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
class MSSQL {
|
|
139
|
+
+bracket quoting
|
|
140
|
+
+at-sign parameters
|
|
141
|
+
+GETUTCDATE timestamps
|
|
142
|
+
+OFFSET FETCH pagination
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
Dialect <|-- MySQL
|
|
146
|
+
Dialect <|-- PostgreSQL
|
|
147
|
+
Dialect <|-- MSSQL
|
|
148
|
+
```
|
|
79
149
|
|
|
80
150
|
The dialect handles all syntax differences: quoting identifiers, parameter prefixes, pagination syntax, date functions, and identity column handling.
|
|
81
151
|
|
|
@@ -85,13 +155,13 @@ Every setter method returns `this`, enabling fluent composition:
|
|
|
85
155
|
|
|
86
156
|
```javascript
|
|
87
157
|
tmpQuery
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
158
|
+
.setScope('Orders')
|
|
159
|
+
.setDataElements(['OrderID', 'Total', 'Status'])
|
|
160
|
+
.addFilter('Status', 'Pending')
|
|
161
|
+
.addSort({Column: 'Total', Direction: 'Descending'})
|
|
162
|
+
.setCap(50)
|
|
163
|
+
.setDialect('MySQL')
|
|
164
|
+
.buildReadQuery();
|
|
95
165
|
```
|
|
96
166
|
|
|
97
167
|
## Fable Integration
|
|
@@ -102,3 +172,25 @@ FoxHound depends on Fable for:
|
|
|
102
172
|
- **Logging** — filter, scope, and query errors are logged through `_Fable.log`
|
|
103
173
|
- **Utility Functions** — `_Fable.Utility.extend()` for parameter merging and `_Fable.Utility.template()` for query overrides
|
|
104
174
|
- **Configuration** — inherits any relevant settings from the Fable config
|
|
175
|
+
|
|
176
|
+
## Schema-Aware Generation
|
|
177
|
+
|
|
178
|
+
```mermaid
|
|
179
|
+
flowchart TD
|
|
180
|
+
A[Record Object] --> B{Schema Attached?}
|
|
181
|
+
B -->|No| C[All columns parameterized]
|
|
182
|
+
B -->|Yes| D[Check each column type]
|
|
183
|
+
D --> E{AutoIdentity?}
|
|
184
|
+
E -->|Yes| F[NULL / DEFAULT / Skip]
|
|
185
|
+
D --> G{CreateDate?}
|
|
186
|
+
G -->|Yes| H[NOW timestamp]
|
|
187
|
+
D --> I{Deleted?}
|
|
188
|
+
I -->|Yes| J[Auto-filter on Read]
|
|
189
|
+
D --> K{Default?}
|
|
190
|
+
K -->|Yes| L[Parameterized value]
|
|
191
|
+
C --> M[Generate SQL]
|
|
192
|
+
F --> M
|
|
193
|
+
H --> M
|
|
194
|
+
J --> M
|
|
195
|
+
L --> M
|
|
196
|
+
```
|
package/docs/dialects/README.md
CHANGED
|
@@ -7,6 +7,7 @@ FoxHound uses a dialect strategy pattern to generate database-specific SQL from
|
|
|
7
7
|
| Dialect | Target | Identifier Quoting | Parameter Prefix | Pagination |
|
|
8
8
|
|---------|--------|-------------------|-----------------|------------|
|
|
9
9
|
| [MySQL](dialects/mysql.md) | MySQL / MariaDB | `` `backticks` `` | `:name` | `LIMIT offset, count` |
|
|
10
|
+
| [PostgreSQL](dialects/postgresql.md) | PostgreSQL 9.5+ | `"double quotes"` | `:name` | `LIMIT count OFFSET offset` |
|
|
10
11
|
| [MSSQL](dialects/mssql.md) | Microsoft SQL Server | `[brackets]` | `@name` | `OFFSET n ROWS FETCH NEXT m ROWS ONLY` |
|
|
11
12
|
| [SQLite](dialects/sqlite.md) | SQLite 3 | `` `backticks` `` | `:name` | `LIMIT count OFFSET offset` |
|
|
12
13
|
| [ALASQL](dialects/alasql.md) | ALASQL (in-memory) | `` `backticks` `` | `:name` | `LIMIT count FETCH offset` |
|
|
@@ -54,6 +55,7 @@ All SQL dialects share these behaviors:
|
|
|
54
55
|
| Use Case | Recommended Dialect |
|
|
55
56
|
|----------|-------------------|
|
|
56
57
|
| Production MySQL/MariaDB | `MySQL` |
|
|
58
|
+
| Production PostgreSQL | `PostgreSQL` |
|
|
57
59
|
| Production SQL Server | `MSSQL` |
|
|
58
60
|
| Embedded/file-based database | `SQLite` |
|
|
59
61
|
| Browser-side or in-memory queries | `ALASQL` |
|