@atscript/db-mysql 0.1.140 → 0.1.142

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/dist/index.cjs CHANGED
@@ -94,9 +94,17 @@ function mysqlIndexPrefix(mappedType, bytesPerChar) {
94
94
  * required default-less column on `add`;
95
95
  * 5. COLLATE (native `@db.mysql.collate` or portable `@db.column.collate`);
96
96
  * 6. ON UPDATE.
97
+ *
98
+ * A `@db.column.derived` field (since 0.1.141) renders as a VIRTUAL generated
99
+ * column — `type [COLLATE …] GENERATED ALWAYS AS (<json extraction>) VIRTUAL`
100
+ * — with no nullability, DEFAULT, AUTO_INCREMENT or ON UPDATE, on every purpose.
97
101
  */
98
102
  function buildColumnDefinition(field, ctx) {
99
103
  const sqlType = ctx.typeMapper?.(field) ?? mysqlTypeFromField(field);
104
+ if (field.derived) return {
105
+ def: `${qi(field.physicalName)} ${sqlType}${mysqlCollateClause(field)} GENERATED ALWAYS AS (${(0, _atscript_db_sql_tools.derivedColumnExpr)(mysqlDialect, field)}) VIRTUAL`,
106
+ inventedDefault: false
107
+ };
100
108
  const increment = ctx.purpose !== "add" && (ctx.incrementFields?.has(field.physicalName) ?? false);
101
109
  let def = `${qi(field.physicalName)} ${sqlType}`;
102
110
  if (increment) def += " AUTO_INCREMENT";
@@ -112,9 +120,7 @@ function buildColumnDefinition(field, ctx) {
112
120
  def += ` DEFAULT ${mysqlTypeDefault(sqlType, field)}`;
113
121
  inventedDefault = true;
114
122
  }
115
- const nativeCollate = field.type?.metadata?.get("db.mysql.collate");
116
- if (nativeCollate) def += ` COLLATE ${nativeCollate}`;
117
- else if (field.collate) def += ` COLLATE ${collationToMysql(field.collate)}`;
123
+ def += mysqlCollateClause(field);
118
124
  const onUpdate = ctx.onUpdateFields?.get(field.physicalName);
119
125
  if (onUpdate) def += ` ON UPDATE ${onUpdate}`;
120
126
  return {
@@ -340,6 +346,15 @@ function buildAggregateCount(table, where, controls) {
340
346
  /**
341
347
  * Maps portable collation values to MySQL collation names.
342
348
  */
349
+ /**
350
+ * The `COLLATE` clause of a column (native `@db.mysql.collate`, else the
351
+ * portable `@db.column.collate` mapping) — `""` when none applies.
352
+ */
353
+ function mysqlCollateClause(field) {
354
+ const nativeCollate = field.type?.metadata?.get("db.mysql.collate");
355
+ if (nativeCollate) return ` COLLATE ${nativeCollate}`;
356
+ return field.collate ? ` COLLATE ${collationToMysql(field.collate)}` : "";
357
+ }
343
358
  function collationToMysql(collation) {
344
359
  switch (collation) {
345
360
  case "binary": return "utf8mb4_bin";
@@ -395,7 +410,7 @@ function mysqlTypeFromField(field) {
395
410
  const maxLen = (metadata?.get("expect.maxLength"))?.length;
396
411
  if (maxLen !== void 0 && maxLen <= 65535) return `VARCHAR(${maxLen})`;
397
412
  if (maxLen !== void 0 && maxLen > 65535) return "LONGTEXT";
398
- if (field.isPrimaryKey || field.defaultValue) return "VARCHAR(255)";
413
+ if (field.isPrimaryKey || field.defaultValue || field.derived) return "VARCHAR(255)";
399
414
  return "TEXT";
400
415
  }
401
416
  case "json":
@@ -858,7 +873,7 @@ var MysqlAdapter = class MysqlAdapter extends _atscript_db.BaseDbAdapter {
858
873
  }
859
874
  async updateOne(filter, data, ops, expectedVersion) {
860
875
  const where = buildWhere(filter);
861
- const versionColumn = this._table.versionColumn;
876
+ const versionColumn = this._table.versionColumnPhysical;
862
877
  const { sql, params } = buildUpdate(this.resolveTableName(), data, where, 1, ops, versionColumn, expectedVersion);
863
878
  this._log(sql, params);
864
879
  const result = await this._wrapConstraintError(() => this._exec().run(sql, params));
@@ -869,7 +884,7 @@ var MysqlAdapter = class MysqlAdapter extends _atscript_db.BaseDbAdapter {
869
884
  }
870
885
  async updateMany(filter, data, ops) {
871
886
  const where = buildWhere(filter);
872
- const versionColumn = this._table.versionColumn;
887
+ const versionColumn = this._table.versionColumnPhysical;
873
888
  const { sql, params } = buildUpdate(this.resolveTableName(), data, where, void 0, ops, versionColumn);
874
889
  this._log(sql, params);
875
890
  const result = await this._wrapConstraintError(() => this._exec().run(sql, params));
@@ -880,7 +895,7 @@ var MysqlAdapter = class MysqlAdapter extends _atscript_db.BaseDbAdapter {
880
895
  }
881
896
  async replaceOne(filter, data, expectedVersion) {
882
897
  const where = buildWhere(filter);
883
- const versionColumn = this._table.versionColumn;
898
+ const versionColumn = this._table.versionColumnPhysical;
884
899
  const full = (0, _atscript_db_sql_tools.fillReplacePayload)(data, (0, _atscript_db_sql_tools.replaceColumnsFor)(this._table.fieldDescriptors, this.nativeDefaultFns()), versionColumn);
885
900
  const { sql, params } = buildUpdate(this.resolveTableName(), full, where, 1, void 0, versionColumn, expectedVersion);
886
901
  this._log(sql, params);
@@ -892,7 +907,7 @@ var MysqlAdapter = class MysqlAdapter extends _atscript_db.BaseDbAdapter {
892
907
  }
893
908
  async replaceMany(filter, data) {
894
909
  const where = buildWhere(filter);
895
- const versionColumn = this._table.versionColumn;
910
+ const versionColumn = this._table.versionColumnPhysical;
896
911
  const { sql, params } = buildUpdate(this.resolveTableName(), data, where, void 0, void 0, versionColumn);
897
912
  this._log(sql, params);
898
913
  const result = await this._wrapConstraintError(() => this._exec().run(sql, params));
@@ -1036,20 +1051,24 @@ var MysqlAdapter = class MysqlAdapter extends _atscript_db.BaseDbAdapter {
1036
1051
  }
1037
1052
  /** Live columns of `tableName`, read through `exec` (a held connection, or the pool). */
1038
1053
  async _readColumns(exec, tableName) {
1039
- return (await exec.all(`SELECT c.COLUMN_NAME, c.COLUMN_TYPE, c.IS_NULLABLE, c.COLUMN_DEFAULT, c.SRS_ID,
1054
+ return (await exec.all(`SELECT c.COLUMN_NAME, c.COLUMN_TYPE, c.IS_NULLABLE, c.COLUMN_DEFAULT, c.SRS_ID, c.EXTRA,
1040
1055
  (k.COLUMN_NAME IS NOT NULL) AS IS_PK
1041
1056
  FROM INFORMATION_SCHEMA.COLUMNS c
1042
1057
  LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE k
1043
1058
  ON k.TABLE_SCHEMA = c.TABLE_SCHEMA AND k.TABLE_NAME = c.TABLE_NAME
1044
1059
  AND k.COLUMN_NAME = c.COLUMN_NAME AND k.CONSTRAINT_NAME = 'PRIMARY'
1045
1060
  WHERE c.TABLE_NAME = ? AND c.TABLE_SCHEMA = COALESCE(?, DATABASE())
1046
- ORDER BY c.ORDINAL_POSITION`, [tableName, this._schema])).map((r) => ({
1047
- name: r.COLUMN_NAME,
1048
- type: r.SRS_ID == null ? r.COLUMN_TYPE.toUpperCase() : `${r.COLUMN_TYPE.toUpperCase()} SRID ${r.SRS_ID}`,
1049
- notnull: r.IS_NULLABLE === "NO",
1050
- pk: Boolean(Number(r.IS_PK ?? 0)),
1051
- dflt_value: normalizeMysqlDefault(r.COLUMN_DEFAULT)
1052
- }));
1061
+ ORDER BY c.ORDINAL_POSITION`, [tableName, this._schema])).map((r) => {
1062
+ const column = {
1063
+ name: r.COLUMN_NAME,
1064
+ type: r.SRS_ID == null ? r.COLUMN_TYPE.toUpperCase() : `${r.COLUMN_TYPE.toUpperCase()} SRID ${r.SRS_ID}`,
1065
+ notnull: r.IS_NULLABLE === "NO",
1066
+ pk: Boolean(Number(r.IS_PK ?? 0)),
1067
+ dflt_value: normalizeMysqlDefault(r.COLUMN_DEFAULT)
1068
+ };
1069
+ if (/\b(VIRTUAL|STORED) GENERATED\b/i.test(r.EXTRA ?? "")) column.generated = true;
1070
+ return column;
1071
+ });
1053
1072
  }
1054
1073
  async syncColumns(diff) {
1055
1074
  return this._withStrictSession(async (conn) => {
@@ -1120,7 +1139,7 @@ var MysqlAdapter = class MysqlAdapter extends _atscript_db.BaseDbAdapter {
1120
1139
  this._log(createSql);
1121
1140
  await conn.exec(createSql);
1122
1141
  const oldCols = (await this._readColumns(conn, this._table.tableName)).map((c) => c.name);
1123
- const newCols = this._table.fieldDescriptors.filter((f) => !f.ignored).map((f) => f.physicalName);
1142
+ const newCols = this._table.storedDescriptors.map((f) => f.physicalName);
1124
1143
  const oldColSet = new Set(oldCols);
1125
1144
  const commonCols = newCols.filter((c) => oldColSet.has(c));
1126
1145
  if (commonCols.length > 0) {
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { ALL_AGGREGATE_FNS, ALL_BUCKET_UNITS, BaseDbAdapter, DbError, DbSpace, bucketTimeZoneUnavailable } from "@atscript/db";
2
2
  import { resolveAggregateSearch } from "@atscript/db/agg";
3
- import { buildAggregateCount, buildAggregateSelect, buildCreateView, buildDelete, buildGeoSearchCount, buildGeoSearchSelect, buildInsert, buildInsertMany, buildSelect, buildUpdate, buildWhere as buildWhere$1, defaultValueForType, defaultValueToSqlLiteral, fillReplacePayload, geoWindowFromControls, insertManyColumns, jsonDollarPath, normalizeGeoPointValue, parseRegexString, refActionToSql, renameGeoDistance, replaceColumnsFor, sqlTimeZoneLiteral, toSqlValue } from "@atscript/db-sql-tools";
3
+ import { buildAggregateCount, buildAggregateSelect, buildCreateView, buildDelete, buildGeoSearchCount, buildGeoSearchSelect, buildInsert, buildInsertMany, buildSelect, buildUpdate, buildWhere as buildWhere$1, defaultValueForType, defaultValueToSqlLiteral, derivedColumnExpr, fillReplacePayload, geoWindowFromControls, insertManyColumns, jsonDollarPath, normalizeGeoPointValue, parseRegexString, refActionToSql, renameGeoDistance, replaceColumnsFor, sqlTimeZoneLiteral, toSqlValue } from "@atscript/db-sql-tools";
4
4
  import { BUCKET_MAX_INSTANT, BUCKET_MIN_INSTANT } from "@uniqu/core";
5
5
  //#region src/sql-builder.ts
6
6
  /** TEXT / BLOB families (`TINYTEXT` … `LONGBLOB`). */
@@ -93,9 +93,17 @@ function mysqlIndexPrefix(mappedType, bytesPerChar) {
93
93
  * required default-less column on `add`;
94
94
  * 5. COLLATE (native `@db.mysql.collate` or portable `@db.column.collate`);
95
95
  * 6. ON UPDATE.
96
+ *
97
+ * A `@db.column.derived` field (since 0.1.141) renders as a VIRTUAL generated
98
+ * column — `type [COLLATE …] GENERATED ALWAYS AS (<json extraction>) VIRTUAL`
99
+ * — with no nullability, DEFAULT, AUTO_INCREMENT or ON UPDATE, on every purpose.
96
100
  */
97
101
  function buildColumnDefinition(field, ctx) {
98
102
  const sqlType = ctx.typeMapper?.(field) ?? mysqlTypeFromField(field);
103
+ if (field.derived) return {
104
+ def: `${qi(field.physicalName)} ${sqlType}${mysqlCollateClause(field)} GENERATED ALWAYS AS (${derivedColumnExpr(mysqlDialect, field)}) VIRTUAL`,
105
+ inventedDefault: false
106
+ };
99
107
  const increment = ctx.purpose !== "add" && (ctx.incrementFields?.has(field.physicalName) ?? false);
100
108
  let def = `${qi(field.physicalName)} ${sqlType}`;
101
109
  if (increment) def += " AUTO_INCREMENT";
@@ -111,9 +119,7 @@ function buildColumnDefinition(field, ctx) {
111
119
  def += ` DEFAULT ${mysqlTypeDefault(sqlType, field)}`;
112
120
  inventedDefault = true;
113
121
  }
114
- const nativeCollate = field.type?.metadata?.get("db.mysql.collate");
115
- if (nativeCollate) def += ` COLLATE ${nativeCollate}`;
116
- else if (field.collate) def += ` COLLATE ${collationToMysql(field.collate)}`;
122
+ def += mysqlCollateClause(field);
117
123
  const onUpdate = ctx.onUpdateFields?.get(field.physicalName);
118
124
  if (onUpdate) def += ` ON UPDATE ${onUpdate}`;
119
125
  return {
@@ -339,6 +345,15 @@ function buildAggregateCount$1(table, where, controls) {
339
345
  /**
340
346
  * Maps portable collation values to MySQL collation names.
341
347
  */
348
+ /**
349
+ * The `COLLATE` clause of a column (native `@db.mysql.collate`, else the
350
+ * portable `@db.column.collate` mapping) — `""` when none applies.
351
+ */
352
+ function mysqlCollateClause(field) {
353
+ const nativeCollate = field.type?.metadata?.get("db.mysql.collate");
354
+ if (nativeCollate) return ` COLLATE ${nativeCollate}`;
355
+ return field.collate ? ` COLLATE ${collationToMysql(field.collate)}` : "";
356
+ }
342
357
  function collationToMysql(collation) {
343
358
  switch (collation) {
344
359
  case "binary": return "utf8mb4_bin";
@@ -394,7 +409,7 @@ function mysqlTypeFromField(field) {
394
409
  const maxLen = (metadata?.get("expect.maxLength"))?.length;
395
410
  if (maxLen !== void 0 && maxLen <= 65535) return `VARCHAR(${maxLen})`;
396
411
  if (maxLen !== void 0 && maxLen > 65535) return "LONGTEXT";
397
- if (field.isPrimaryKey || field.defaultValue) return "VARCHAR(255)";
412
+ if (field.isPrimaryKey || field.defaultValue || field.derived) return "VARCHAR(255)";
398
413
  return "TEXT";
399
414
  }
400
415
  case "json":
@@ -857,7 +872,7 @@ var MysqlAdapter = class MysqlAdapter extends BaseDbAdapter {
857
872
  }
858
873
  async updateOne(filter, data, ops, expectedVersion) {
859
874
  const where = buildWhere(filter);
860
- const versionColumn = this._table.versionColumn;
875
+ const versionColumn = this._table.versionColumnPhysical;
861
876
  const { sql, params } = buildUpdate$1(this.resolveTableName(), data, where, 1, ops, versionColumn, expectedVersion);
862
877
  this._log(sql, params);
863
878
  const result = await this._wrapConstraintError(() => this._exec().run(sql, params));
@@ -868,7 +883,7 @@ var MysqlAdapter = class MysqlAdapter extends BaseDbAdapter {
868
883
  }
869
884
  async updateMany(filter, data, ops) {
870
885
  const where = buildWhere(filter);
871
- const versionColumn = this._table.versionColumn;
886
+ const versionColumn = this._table.versionColumnPhysical;
872
887
  const { sql, params } = buildUpdate$1(this.resolveTableName(), data, where, void 0, ops, versionColumn);
873
888
  this._log(sql, params);
874
889
  const result = await this._wrapConstraintError(() => this._exec().run(sql, params));
@@ -879,7 +894,7 @@ var MysqlAdapter = class MysqlAdapter extends BaseDbAdapter {
879
894
  }
880
895
  async replaceOne(filter, data, expectedVersion) {
881
896
  const where = buildWhere(filter);
882
- const versionColumn = this._table.versionColumn;
897
+ const versionColumn = this._table.versionColumnPhysical;
883
898
  const full = fillReplacePayload(data, replaceColumnsFor(this._table.fieldDescriptors, this.nativeDefaultFns()), versionColumn);
884
899
  const { sql, params } = buildUpdate$1(this.resolveTableName(), full, where, 1, void 0, versionColumn, expectedVersion);
885
900
  this._log(sql, params);
@@ -891,7 +906,7 @@ var MysqlAdapter = class MysqlAdapter extends BaseDbAdapter {
891
906
  }
892
907
  async replaceMany(filter, data) {
893
908
  const where = buildWhere(filter);
894
- const versionColumn = this._table.versionColumn;
909
+ const versionColumn = this._table.versionColumnPhysical;
895
910
  const { sql, params } = buildUpdate$1(this.resolveTableName(), data, where, void 0, void 0, versionColumn);
896
911
  this._log(sql, params);
897
912
  const result = await this._wrapConstraintError(() => this._exec().run(sql, params));
@@ -1035,20 +1050,24 @@ var MysqlAdapter = class MysqlAdapter extends BaseDbAdapter {
1035
1050
  }
1036
1051
  /** Live columns of `tableName`, read through `exec` (a held connection, or the pool). */
1037
1052
  async _readColumns(exec, tableName) {
1038
- return (await exec.all(`SELECT c.COLUMN_NAME, c.COLUMN_TYPE, c.IS_NULLABLE, c.COLUMN_DEFAULT, c.SRS_ID,
1053
+ return (await exec.all(`SELECT c.COLUMN_NAME, c.COLUMN_TYPE, c.IS_NULLABLE, c.COLUMN_DEFAULT, c.SRS_ID, c.EXTRA,
1039
1054
  (k.COLUMN_NAME IS NOT NULL) AS IS_PK
1040
1055
  FROM INFORMATION_SCHEMA.COLUMNS c
1041
1056
  LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE k
1042
1057
  ON k.TABLE_SCHEMA = c.TABLE_SCHEMA AND k.TABLE_NAME = c.TABLE_NAME
1043
1058
  AND k.COLUMN_NAME = c.COLUMN_NAME AND k.CONSTRAINT_NAME = 'PRIMARY'
1044
1059
  WHERE c.TABLE_NAME = ? AND c.TABLE_SCHEMA = COALESCE(?, DATABASE())
1045
- ORDER BY c.ORDINAL_POSITION`, [tableName, this._schema])).map((r) => ({
1046
- name: r.COLUMN_NAME,
1047
- type: r.SRS_ID == null ? r.COLUMN_TYPE.toUpperCase() : `${r.COLUMN_TYPE.toUpperCase()} SRID ${r.SRS_ID}`,
1048
- notnull: r.IS_NULLABLE === "NO",
1049
- pk: Boolean(Number(r.IS_PK ?? 0)),
1050
- dflt_value: normalizeMysqlDefault(r.COLUMN_DEFAULT)
1051
- }));
1060
+ ORDER BY c.ORDINAL_POSITION`, [tableName, this._schema])).map((r) => {
1061
+ const column = {
1062
+ name: r.COLUMN_NAME,
1063
+ type: r.SRS_ID == null ? r.COLUMN_TYPE.toUpperCase() : `${r.COLUMN_TYPE.toUpperCase()} SRID ${r.SRS_ID}`,
1064
+ notnull: r.IS_NULLABLE === "NO",
1065
+ pk: Boolean(Number(r.IS_PK ?? 0)),
1066
+ dflt_value: normalizeMysqlDefault(r.COLUMN_DEFAULT)
1067
+ };
1068
+ if (/\b(VIRTUAL|STORED) GENERATED\b/i.test(r.EXTRA ?? "")) column.generated = true;
1069
+ return column;
1070
+ });
1052
1071
  }
1053
1072
  async syncColumns(diff) {
1054
1073
  return this._withStrictSession(async (conn) => {
@@ -1119,7 +1138,7 @@ var MysqlAdapter = class MysqlAdapter extends BaseDbAdapter {
1119
1138
  this._log(createSql);
1120
1139
  await conn.exec(createSql);
1121
1140
  const oldCols = (await this._readColumns(conn, this._table.tableName)).map((c) => c.name);
1122
- const newCols = this._table.fieldDescriptors.filter((f) => !f.ignored).map((f) => f.physicalName);
1141
+ const newCols = this._table.storedDescriptors.map((f) => f.physicalName);
1123
1142
  const oldColSet = new Set(oldCols);
1124
1143
  const commonCols = newCols.filter((c) => oldColSet.has(c));
1125
1144
  if (commonCols.length > 0) {
package/dist/plugin.cjs CHANGED
@@ -14,6 +14,7 @@ const annotations = {
14
14
  engine: new _atscript_core.AnnotationSpec({
15
15
  description: "Specifies the MySQL storage engine.\n\n**Default:** `\"InnoDB\"`\n\n```atscript\n@db.mysql.engine \"MyISAM\"\nexport interface Logs { ... }\n```",
16
16
  nodeType: ["interface"],
17
+ passedWhenReferred: false,
17
18
  multiple: false,
18
19
  argument: {
19
20
  name: "engine",
package/dist/plugin.mjs CHANGED
@@ -13,6 +13,7 @@ const annotations = {
13
13
  engine: new AnnotationSpec({
14
14
  description: "Specifies the MySQL storage engine.\n\n**Default:** `\"InnoDB\"`\n\n```atscript\n@db.mysql.engine \"MyISAM\"\nexport interface Logs { ... }\n```",
15
15
  nodeType: ["interface"],
16
+ passedWhenReferred: false,
16
17
  multiple: false,
17
18
  argument: {
18
19
  name: "engine",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atscript/db-mysql",
3
- "version": "0.1.140",
3
+ "version": "0.1.142",
4
4
  "description": "MySQL adapter for @atscript/db with mysql2 driver support.",
5
5
  "keywords": [
6
6
  "atscript",
@@ -41,19 +41,19 @@
41
41
  "access": "public"
42
42
  },
43
43
  "devDependencies": {
44
- "@atscript/core": "^0.1.92",
45
- "@atscript/typescript": "^0.1.92",
44
+ "@atscript/core": "^0.1.95",
45
+ "@atscript/typescript": "^0.1.95",
46
46
  "@uniqu/core": "^0.1.11",
47
47
  "mysql2": "^3.11.0",
48
- "unplugin-atscript": "^0.1.92"
48
+ "unplugin-atscript": "^0.1.95"
49
49
  },
50
50
  "peerDependencies": {
51
- "@atscript/core": "^0.1.92",
52
- "@atscript/typescript": "^0.1.92",
51
+ "@atscript/core": "^0.1.95",
52
+ "@atscript/typescript": "^0.1.95",
53
53
  "@uniqu/core": "^0.1.11",
54
54
  "mysql2": ">=3.0.0",
55
- "@atscript/db": "^0.1.140",
56
- "@atscript/db-sql-tools": "^0.1.140"
55
+ "@atscript/db": "^0.1.142",
56
+ "@atscript/db-sql-tools": "^0.1.142"
57
57
  },
58
58
  "peerDependenciesMeta": {
59
59
  "mysql2": {