Skip to content

Commit 13e185d

Browse files
committed
feat: Add support for key prefix lengths
1 parent 673fad7 commit 13e185d

File tree

10 files changed

+258
-48
lines changed

10 files changed

+258
-48
lines changed

README.md

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1125,9 +1125,9 @@ See the [Column Types](#column-types) section for all possible column types and
11251125

11261126
### Primary Key
11271127

1128-
`string|string[]`
1128+
`string | string[]`
11291129

1130-
The table’s primary key can be defined with the `primaryKey` property:
1130+
The table’s primary key can be defined with the `primaryKey` property.
11311131

11321132
```js
11331133
{
@@ -1151,6 +1151,17 @@ An array can be used to define a multi-column primary key.
11511151
}
11521152
```
11531153

1154+
Primary keys for string columns may include a key [prefix length](#prefix-lengths).
1155+
1156+
```js
1157+
{
1158+
columns: {
1159+
id: pool.ColTypes.varchar(100).unsigned().notNull(),
1160+
},
1161+
primaryKey: 'id(20)'
1162+
}
1163+
```
1164+
11541165
### Keys
11551166

11561167
Keys can be defined with the `keys` property, which is an array of [`KeyTypes`](#key-types):
@@ -1395,10 +1406,10 @@ Compatible types:
13951406

13961407
[`mysql.KeyTypes`](#module_mysql-plus..KeyTypes) and [`pool.KeyTypes`](#PoolPlus+KeyTypes) both expose the following methods for defining table keys:
13971408

1398-
+ `index(columnName [, ...otherColumns])` - Creates a regular [index](https://dev.mysql.com/doc/en/create-index.html)
1399-
+ `uniqueIndex(columnName [, ...otherColumns])` - Creates a [unique index](https://dev.mysql.com/doc/en/create-index.html#create-index-unique)
1409+
+ `index(keyPart [, ...otherKeyParts])` - Creates a regular [index](https://dev.mysql.com/doc/en/create-index.html)
1410+
+ `uniqueIndex(keyPart [, ...otherKeyParts])` - Creates a [unique index](https://dev.mysql.com/doc/en/create-index.html#create-index-unique)
14001411
+ `spatialIndex(columnName)` - Creates a [spatial index](https://dev.mysql.com/doc/en/create-index.html#create-index-spatial)
1401-
+ `fulltextIndex(columnName)` - Creates a [fulltext index](https://dev.mysql.com/doc/en/innodb-fulltext-index.html)
1412+
+ `fulltextIndex(columnName, [...otherColumns])` - Creates a [fulltext index](https://dev.mysql.com/doc/en/innodb-fulltext-index.html)
14021413
+ `foreignKey(columnName [, ...otherColumns])` - Creates a [foreign key constraint](https://dev.mysql.com/doc/en/create-table-foreign-keys.html)
14031414

14041415
**Example:**
@@ -1473,4 +1484,22 @@ Indexes required for the example above:
14731484
KeyTypes.index('thingOne', 'thingTwo'),
14741485
]
14751486
}
1476-
```
1487+
```
1488+
1489+
### Prefix Lengths
1490+
1491+
`PRIMARY`, `INDEX`, and `UNIQUE` keys on `char`, `varchar`, `binary`, `varbinary`, `blob`, and `text` columns may include a [key prefix length](https://dev.mysql.com/doc/en/create-index.html#create-index-column-prefixes).
1492+
1493+
```js
1494+
{
1495+
columns: {
1496+
id: ColTypes.char(50).notNull(),
1497+
uid: ColTypes.varchar(100).notNull(),
1498+
description: ColTypes.text(),
1499+
},
1500+
primaryKey: 'id(10)',
1501+
keys: [
1502+
KeyTypes.uniqueIndex('uid(30)'),
1503+
KeyTypes.index('description(50)'),
1504+
]
1505+
```

jsdoc2md/README.hbs

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -166,9 +166,9 @@ See the [Column Types](#column-types) section for all possible column types and
166166

167167
### Primary Key
168168

169-
`string|string[]`
169+
`string | string[]`
170170

171-
The table’s primary key can be defined with the `primaryKey` property:
171+
The table’s primary key can be defined with the `primaryKey` property.
172172

173173
```js
174174
{
@@ -192,6 +192,17 @@ An array can be used to define a multi-column primary key.
192192
}
193193
```
194194

195+
Primary keys for string columns may include a key [prefix length](#prefix-lengths).
196+
197+
```js
198+
{
199+
columns: {
200+
id: pool.ColTypes.varchar(100).unsigned().notNull(),
201+
},
202+
primaryKey: 'id(20)'
203+
}
204+
```
205+
195206
### Keys
196207

197208
Keys can be defined with the `keys` property, which is an array of [`KeyTypes`](#key-types):
@@ -436,10 +447,10 @@ Compatible types:
436447

437448
[`mysql.KeyTypes`](#module_mysql-plus..KeyTypes) and [`pool.KeyTypes`](#PoolPlus+KeyTypes) both expose the following methods for defining table keys:
438449

439-
+ `index(columnName [, ...otherColumns])` - Creates a regular [index](https://dev.mysql.com/doc/en/create-index.html)
440-
+ `uniqueIndex(columnName [, ...otherColumns])` - Creates a [unique index](https://dev.mysql.com/doc/en/create-index.html#create-index-unique)
450+
+ `index(keyPart [, ...otherKeyParts])` - Creates a regular [index](https://dev.mysql.com/doc/en/create-index.html)
451+
+ `uniqueIndex(keyPart [, ...otherKeyParts])` - Creates a [unique index](https://dev.mysql.com/doc/en/create-index.html#create-index-unique)
441452
+ `spatialIndex(columnName)` - Creates a [spatial index](https://dev.mysql.com/doc/en/create-index.html#create-index-spatial)
442-
+ `fulltextIndex(columnName)` - Creates a [fulltext index](https://dev.mysql.com/doc/en/innodb-fulltext-index.html)
453+
+ `fulltextIndex(columnName, [...otherColumns])` - Creates a [fulltext index](https://dev.mysql.com/doc/en/innodb-fulltext-index.html)
443454
+ `foreignKey(columnName [, ...otherColumns])` - Creates a [foreign key constraint](https://dev.mysql.com/doc/en/create-table-foreign-keys.html)
444455

445456
**Example:**
@@ -514,4 +525,22 @@ Indexes required for the example above:
514525
KeyTypes.index('thingOne', 'thingTwo'),
515526
]
516527
}
517-
```
528+
```
529+
530+
### Prefix Lengths
531+
532+
`PRIMARY`, `INDEX`, and `UNIQUE` keys on `char`, `varchar`, `binary`, `varbinary`, `blob`, and `text` columns may include a [key prefix length](https://dev.mysql.com/doc/en/create-index.html#create-index-column-prefixes).
533+
534+
```js
535+
{
536+
columns: {
537+
id: ColTypes.char(50).notNull(),
538+
uid: ColTypes.varchar(100).notNull(),
539+
description: ColTypes.text(),
540+
},
541+
primaryKey: 'id(10)',
542+
keys: [
543+
KeyTypes.uniqueIndex('uid(30)'),
544+
KeyTypes.index('description(50)'),
545+
]
546+
```

lib/KeyDefinitions/IndexKeyDefinition.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22

33
const arraysEqual = require('../utils/arraysEqual');
44
const {escapeId} = require('mysql/lib/protocol/SqlString');
5+
const parseKeyParts = require('../utils/parseKeyParts');
56

67
class IndexKeyDefinition {
7-
constructor(type, namePrefix, columns) {
8-
this.$name = namePrefix + '_' + columns.join('_');
8+
constructor(type, namePrefix, keyParts) {
9+
const {columnNames, formattedKeyParts} = parseKeyParts(keyParts);
10+
this.$name = namePrefix + '_' + columnNames.join('_');
911
this._type = type;
10-
this._columns = columns;
12+
this._keyParts = formattedKeyParts;
1113
}
1214

1315
name(name) {
@@ -18,11 +20,11 @@ class IndexKeyDefinition {
1820
$equals(otherKey) {
1921
return this._type === otherKey._type &&
2022
this.$name === otherKey.$name &&
21-
arraysEqual(this._columns, otherKey._columns);
23+
arraysEqual(this._keyParts, otherKey._keyParts);
2224
}
2325

2426
$toSQL() {
25-
return `${this._type} ${escapeId(this.$name)} (${escapeId(this._columns)})`;
27+
return `${this._type} ${escapeId(this.$name)} (${this._keyParts.join(', ')})`;
2628
}
2729
}
2830

lib/KeyDefinitions/PrimaryKeyDefinition.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
'use strict';
22

33
const arraysEqual = require('../utils/arraysEqual');
4-
const {escapeId} = require('mysql/lib/protocol/SqlString');
4+
const parseKeyParts = require('../utils/parseKeyParts');
55

66
class PrimaryKeyDefinition {
7-
constructor(columns) {
8-
this._columns = columns;
7+
constructor(keyParts) {
8+
const {columnNames, formattedKeyParts} = parseKeyParts(keyParts);
9+
this.$columnNames = columnNames;
10+
this._keyParts = formattedKeyParts;
911
}
1012

1113
$equals(otherKey) {
12-
return arraysEqual(this._columns, otherKey._columns);
14+
return arraysEqual(this._keyParts, otherKey._keyParts);
1315
}
1416

1517
$toSQL() {
16-
return `PRIMARY KEY (${escapeId(this._columns)})`;
18+
return `PRIMARY KEY (${this._keyParts.join(', ')})`;
1719
}
1820
}
1921

lib/TableDefinition.js

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,6 @@ class TableDefinition {
130130
`CHANGE COLUMN ${pool.escapeId(oldColumnName)} ${pool.escapeId(columnName)} ` +
131131
newColumnDefinition.$toSQL() + position
132132
));
133-
134133
renamedColumns.push(oldColumnName);
135134
} else if (!newColumnDefinition.$equals(oldColumnDefinition, oldSchema)) {
136135
operations.push(Operation.create(
@@ -390,14 +389,14 @@ function createInternalSchema(schema, tableName) {
390389
}
391390
}
392391

393-
let primaryKey = null;
392+
let columnPK = null;
394393

395394
// Extract keys defined in the columns
396395
for (const columnName in columns) {
397396
const column = columns[columnName];
398397

399398
if (column.$primaryKey) {
400-
primaryKey = columnName;
399+
columnPK = columnName;
401400
}
402401
if (column.$unique) {
403402
const key = KeyDefinitions.uniqueIndex(columnName);
@@ -417,24 +416,20 @@ function createInternalSchema(schema, tableName) {
417416
}
418417
}
419418

420-
if (schema.primaryKey) {
419+
let primaryKey = null;
420+
421+
if (schema.primaryKey) { // The primaryKey in the schema takes precedence over one defined by a column
422+
if (typeof schema.primaryKey === 'string') {
423+
primaryKey = new PrimaryKeyDefinition([schema.primaryKey]);
424+
} else { // schema.primaryKey is an Array
425+
primaryKey = new PrimaryKeyDefinition(schema.primaryKey);
426+
}
421427
// Make sure primary key columns are not null
422-
if (Array.isArray(schema.primaryKey)) {
423-
for (const colName of schema.primaryKey) {
424-
columns[colName].notNull();
425-
}
426-
} else {
427-
columns[schema.primaryKey].notNull();
428+
for (const colName of primaryKey.$columnNames) {
429+
columns[colName].notNull();
428430
}
429-
430-
// The primaryKey in the schema takes precedence over one defined by a column
431-
primaryKey = schema.primaryKey;
432-
}
433-
434-
if (typeof primaryKey === 'string') {
435-
primaryKey = new PrimaryKeyDefinition([primaryKey]);
436-
} else if (Array.isArray(primaryKey)) {
437-
primaryKey = new PrimaryKeyDefinition(primaryKey);
431+
} else if (columnPK !== null) {
432+
primaryKey = new PrimaryKeyDefinition([columnPK]);
438433
}
439434

440435
return Object.assign({}, schema, {primaryKey, indexKeys, foreignKeys});

lib/sqlToSchema.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -150,20 +150,20 @@ function generateColumnsSchema(createDefinitions) {
150150
}
151151

152152
function columnsSQLToSchema(sql) {
153-
return sql.replace(/`|\s/g, '').split(',');
153+
return sql.replace(/`/g, '').split(/,\s*/);
154154
}
155155

156156
function generatePrimaryKeySchema(keySQL) {
157-
const pkMatch = /^\s*PRIMARY KEY \((.*?)\)/.exec(keySQL);
157+
const pkMatch = /^\s*PRIMARY KEY \((.*)\)/.exec(keySQL);
158158
return pkMatch === null
159159
? null
160160
: new PrimaryKeyDefinition(columnsSQLToSchema(pkMatch[1]));
161161
}
162162

163-
const rgxUniqueKey = /^\s*UNIQUE KEY `(\w+)` \((.*?)\)/;
164-
const rgxIndexKey = /^\s*KEY `(\w+)` \((.*?)\)/;
165-
const rgxSpatialKey = /^\s*SPATIAL KEY `(\w+)` \((.*?)\)/;
166-
const rgxFulltextKey = /^\s*FULLTEXT KEY `(\w+)` \((.*?)\)/;
163+
const rgxUniqueKey = /^\s*UNIQUE KEY `(\w+)` \((.*)\)/;
164+
const rgxIndexKey = /^\s*KEY `(\w+)` \((.*)\)/;
165+
const rgxSpatialKey = /^\s*SPATIAL KEY `(\w+)` \((.*)\)/;
166+
const rgxFulltextKey = /^\s*FULLTEXT KEY `(\w+)` \((.*)\)/;
167167
const rgxForeignKey =
168168
/\s*CONSTRAINT `(\w+)` FOREIGN KEY \(`(.*?)`\) REFERENCES `(\w+)` \(`(.*?)`\)(?: ON DELETE (RESTRICT|CASCADE|SET NULL|NO ACTION))?(?: ON UPDATE (RESTRICT|CASCADE|SET NULL|NO ACTION))?/;
169169

lib/utils/parseKeyParts.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
'use strict';
2+
3+
const {escapeId} = require('mysql/lib/protocol/SqlString');
4+
5+
function parseKeyParts(keyParts) {
6+
const columnNames = [];
7+
const formattedKeyParts = [];
8+
9+
for (let i = 0; i < keyParts.length; i++) {
10+
const parsedKeyPart = parseKeyPart(keyParts[i]);
11+
12+
columnNames.push(parsedKeyPart.column);
13+
formattedKeyParts.push(formatKeyPart(parsedKeyPart));
14+
}
15+
16+
return {columnNames, formattedKeyParts};
17+
}
18+
19+
const rgxKeyPart = /^(\w+)(?:\s*\((\d+)\))?(?:\s+(ASC|DESC))?/i;
20+
21+
function parseKeyPart(keyPart) {
22+
const match = rgxKeyPart.exec(keyPart);
23+
24+
if (match === null) {
25+
throw new Error('Invalid key part: ' + keyPart);
26+
}
27+
28+
return {
29+
column: match[1],
30+
length: match[2],
31+
};
32+
}
33+
34+
function formatKeyPart(keyPart) {
35+
let sql = escapeId(keyPart.column);
36+
37+
if (keyPart.length !== undefined) {
38+
sql += `(${keyPart.length})`;
39+
}
40+
41+
return sql;
42+
}
43+
44+
module.exports = parseKeyParts;

0 commit comments

Comments
 (0)