@atscript/db-mysql 0.1.104 → 0.1.106
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 +158 -12
- package/dist/index.d.cts +18 -2
- package/dist/index.d.mts +18 -2
- package/dist/index.mjs +160 -13
- package/dist/plugin.cjs +93 -2
- package/dist/plugin.d.cts +5 -1
- package/dist/plugin.d.mts +5 -1
- package/dist/plugin.mjs +92 -1
- package/package.json +8 -8
- package/dist/index-BLQr6loH.d.cts +0 -6
- package/dist/index-BLQr6loH.d.mts +0 -6
- package/dist/plugin-AzwuXV59.cjs +0 -98
- package/dist/plugin-CPJY5K_I.mjs +0 -93
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_plugin = require("./plugin-AzwuXV59.cjs");
|
|
3
2
|
let _atscript_db = require("@atscript/db");
|
|
4
3
|
let _atscript_db_sql_tools = require("@atscript/db-sql-tools");
|
|
5
4
|
//#region src/sql-builder.ts
|
|
@@ -40,9 +39,53 @@ const mysqlDialect = {
|
|
|
40
39
|
params: [pattern]
|
|
41
40
|
};
|
|
42
41
|
},
|
|
42
|
+
geoWithin(quotedCol, circle) {
|
|
43
|
+
const dist = mysqlGeoDistanceExpr(quotedCol, circle.center);
|
|
44
|
+
return {
|
|
45
|
+
sql: `${dist.sql} <= ?`,
|
|
46
|
+
params: [...dist.params, circle.radius]
|
|
47
|
+
};
|
|
48
|
+
},
|
|
43
49
|
createViewPrefix: "CREATE OR REPLACE VIEW"
|
|
44
50
|
};
|
|
45
51
|
/**
|
|
52
|
+
* Spherical distance in meters from a POINT column to a query point.
|
|
53
|
+
* Used as the `distExpr` of the shared geo search builder.
|
|
54
|
+
*/
|
|
55
|
+
function mysqlGeoDistanceExpr(quotedCol, point) {
|
|
56
|
+
return {
|
|
57
|
+
sql: `ST_Distance_Sphere(${quotedCol}, ST_SRID(POINT(?, ?), 4326))`,
|
|
58
|
+
params: [point[0], point[1]]
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Encodes a `[lng, lat]` tuple in MySQL's internal geometry format
|
|
63
|
+
* (4-byte SRID LE + WKB point) — geometry columns accept it directly as a
|
|
64
|
+
* binary parameter, bypassing the WKT/WKB axis-order pitfalls entirely.
|
|
65
|
+
*/
|
|
66
|
+
function geoPointToMysqlInternal(point) {
|
|
67
|
+
const buf = Buffer.alloc(25);
|
|
68
|
+
buf.writeUInt32LE(4326, 0);
|
|
69
|
+
buf.writeUInt8(1, 4);
|
|
70
|
+
buf.writeUInt32LE(1, 5);
|
|
71
|
+
buf.writeDoubleLE(point[0], 9);
|
|
72
|
+
buf.writeDoubleLE(point[1], 17);
|
|
73
|
+
return buf;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Decodes a geo read value back to `[lng, lat]`. The mysql2 driver parses
|
|
77
|
+
* POINT columns to `{x, y}` objects (internal order: x=lng, y=lat); raw
|
|
78
|
+
* internal-format buffers are handled for drivers that don't.
|
|
79
|
+
*/
|
|
80
|
+
function mysqlGeoValueToPoint(value) {
|
|
81
|
+
if (typeof value === "object" && value !== null && typeof value.x === "number" && typeof value.y === "number") return [value.x, value.y];
|
|
82
|
+
if (value instanceof Uint8Array && value.length >= 25) {
|
|
83
|
+
const buf = Buffer.isBuffer(value) ? value : Buffer.from(value);
|
|
84
|
+
const littleEndian = buf.readUInt8(4) === 1;
|
|
85
|
+
if (((littleEndian ? buf.readUInt32LE(5) : buf.readUInt32BE(5)) & 255) === 1) return [littleEndian ? buf.readDoubleLE(9) : buf.readDoubleBE(9), littleEndian ? buf.readDoubleLE(17) : buf.readDoubleBE(17)];
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
46
89
|
* Builds an INSERT statement.
|
|
47
90
|
*/
|
|
48
91
|
function buildInsert(table, data) {
|
|
@@ -122,6 +165,7 @@ function mysqlTypeFromField(field) {
|
|
|
122
165
|
const metadata = field.type?.metadata;
|
|
123
166
|
const mysqlTypeOverride = metadata?.get("db.mysql.type");
|
|
124
167
|
if (mysqlTypeOverride) return mysqlTypeOverride;
|
|
168
|
+
if (field.isGeoPoint && !field.encrypted) return "POINT SRID 4326";
|
|
125
169
|
const unsigned = metadata?.has("db.mysql.unsigned") ?? false;
|
|
126
170
|
const precision = metadata?.get("db.column.precision");
|
|
127
171
|
switch (field.designType) {
|
|
@@ -410,6 +454,13 @@ var MysqlAdapter = class MysqlAdapter extends _atscript_db.BaseDbAdapter {
|
|
|
410
454
|
toStorage: (value) => typeof value === "number" ? epochMsToUtcDatetime(value) : value,
|
|
411
455
|
fromStorage: utcDatetimeToEpochMs
|
|
412
456
|
};
|
|
457
|
+
if (field.isGeoPoint && !field.encrypted) return {
|
|
458
|
+
toStorage: (value) => {
|
|
459
|
+
const point = (0, _atscript_db_sql_tools.normalizeGeoPointValue)(value);
|
|
460
|
+
return point ? geoPointToMysqlInternal(point) : value;
|
|
461
|
+
},
|
|
462
|
+
fromStorage: (value) => mysqlGeoValueToPoint(value) ?? value
|
|
463
|
+
};
|
|
413
464
|
}
|
|
414
465
|
/**
|
|
415
466
|
* Wraps an async write operation to catch MySQL constraint errors
|
|
@@ -579,8 +630,11 @@ var MysqlAdapter = class MysqlAdapter extends _atscript_db.BaseDbAdapter {
|
|
|
579
630
|
this._log(sql, params);
|
|
580
631
|
return { deletedCount: (await this._wrapConstraintError(() => this._exec().run(sql, params))).affectedRows };
|
|
581
632
|
}
|
|
582
|
-
async
|
|
633
|
+
async prepareTypeMapper() {
|
|
583
634
|
if (this._supportsVector === void 0 && this._vectorFields.size > 0) await this._detectVectorSupport();
|
|
635
|
+
}
|
|
636
|
+
async ensureTable() {
|
|
637
|
+
await this.prepareTypeMapper();
|
|
584
638
|
if (this._table instanceof _atscript_db.AtscriptDbView) return this._ensureView();
|
|
585
639
|
const sql = buildCreateTable(this.resolveTableName(), this._table.fieldDescriptors, this._table.foreignKeys, {
|
|
586
640
|
engine: this._engine,
|
|
@@ -604,12 +658,12 @@ var MysqlAdapter = class MysqlAdapter extends _atscript_db.BaseDbAdapter {
|
|
|
604
658
|
}
|
|
605
659
|
async getExistingColumnsForTable(tableName) {
|
|
606
660
|
const schema = this._schema;
|
|
607
|
-
return (await this._exec().all(`SELECT COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_KEY, COLUMN_DEFAULT
|
|
661
|
+
return (await this._exec().all(`SELECT COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_KEY, COLUMN_DEFAULT, SRS_ID
|
|
608
662
|
FROM INFORMATION_SCHEMA.COLUMNS
|
|
609
663
|
WHERE TABLE_NAME = ? AND TABLE_SCHEMA = COALESCE(?, DATABASE())
|
|
610
664
|
ORDER BY ORDINAL_POSITION`, [tableName, schema])).map((r) => ({
|
|
611
665
|
name: r.COLUMN_NAME,
|
|
612
|
-
type: r.COLUMN_TYPE.toUpperCase()
|
|
666
|
+
type: r.SRS_ID == null ? r.COLUMN_TYPE.toUpperCase() : `${r.COLUMN_TYPE.toUpperCase()} SRID ${r.SRS_ID}`,
|
|
613
667
|
notnull: r.IS_NULLABLE === "NO",
|
|
614
668
|
pk: r.COLUMN_KEY === "PRI",
|
|
615
669
|
dflt_value: normalizeMysqlDefault(r.COLUMN_DEFAULT)
|
|
@@ -630,7 +684,7 @@ var MysqlAdapter = class MysqlAdapter extends _atscript_db.BaseDbAdapter {
|
|
|
630
684
|
let ddl = `ALTER TABLE ${quoteTableName(tableName)} ADD COLUMN ${qi(field.physicalName)} ${sqlType}`;
|
|
631
685
|
if (!field.optional && !field.isPrimaryKey) ddl += " NOT NULL";
|
|
632
686
|
if (field.defaultValue?.kind === "value") ddl += ` DEFAULT ${(0, _atscript_db_sql_tools.defaultValueToSqlLiteral)(field.designType, field.defaultValue.value)}`;
|
|
633
|
-
else if (!field.optional && !field.isPrimaryKey) ddl += ` DEFAULT ${(0, _atscript_db_sql_tools.defaultValueForType)(field.designType)}`;
|
|
687
|
+
else if (!field.optional && !field.isPrimaryKey) ddl += field.isGeoPoint && !field.encrypted ? ` DEFAULT (ST_SRID(POINT(0, 0), 4326))` : ` DEFAULT ${(0, _atscript_db_sql_tools.defaultValueForType)(field.designType)}`;
|
|
634
688
|
if (field.collate) {
|
|
635
689
|
const nativeCollate = field.type?.metadata?.get("db.mysql.collate");
|
|
636
690
|
ddl += ` COLLATE ${nativeCollate ?? collationToMysql(field.collate)}`;
|
|
@@ -641,6 +695,10 @@ var MysqlAdapter = class MysqlAdapter extends _atscript_db.BaseDbAdapter {
|
|
|
641
695
|
}
|
|
642
696
|
for (const { field } of diff.typeChanged ?? []) {
|
|
643
697
|
const sqlType = this.typeMapper(field);
|
|
698
|
+
if (field.isGeoPoint && !field.encrypted && sqlType.startsWith("POINT")) {
|
|
699
|
+
await this._migrateJsonColumnToPoint(tableName, field);
|
|
700
|
+
continue;
|
|
701
|
+
}
|
|
644
702
|
let ddl = `ALTER TABLE ${quoteTableName(tableName)} MODIFY COLUMN ${qi(field.physicalName)} ${sqlType}`;
|
|
645
703
|
if (!field.optional && !field.isPrimaryKey) ddl += " NOT NULL";
|
|
646
704
|
this._log(ddl);
|
|
@@ -727,6 +785,21 @@ var MysqlAdapter = class MysqlAdapter extends _atscript_db.BaseDbAdapter {
|
|
|
727
785
|
this._log(ddl);
|
|
728
786
|
await this._exec().exec(ddl);
|
|
729
787
|
}
|
|
788
|
+
async dropIndexesForColumns(columns) {
|
|
789
|
+
const placeholders = columns.map(() => "?").join(", ");
|
|
790
|
+
const rows = await this._exec().all(`SELECT DISTINCT INDEX_NAME AS name FROM INFORMATION_SCHEMA.STATISTICS
|
|
791
|
+
WHERE TABLE_NAME = ? AND TABLE_SCHEMA = COALESCE(?, DATABASE())
|
|
792
|
+
AND INDEX_NAME LIKE 'atscript\\_\\_%' AND COLUMN_NAME IN (${placeholders})`, [
|
|
793
|
+
this._table.tableName,
|
|
794
|
+
this._schema,
|
|
795
|
+
...columns
|
|
796
|
+
]);
|
|
797
|
+
for (const row of rows) {
|
|
798
|
+
const sql = `DROP INDEX ${qi(row.name)} ON ${quoteTableName(this.resolveTableName())}`;
|
|
799
|
+
this._log(sql);
|
|
800
|
+
await this._exec().exec(sql);
|
|
801
|
+
}
|
|
802
|
+
}
|
|
730
803
|
async dropTableByName(tableName) {
|
|
731
804
|
const ddl = `DROP TABLE IF EXISTS ${quoteTableName(tableName)}`;
|
|
732
805
|
this._log(ddl);
|
|
@@ -763,9 +836,28 @@ var MysqlAdapter = class MysqlAdapter extends _atscript_db.BaseDbAdapter {
|
|
|
763
836
|
const schema = this._schema;
|
|
764
837
|
const stringFields = new Set(this._table.fieldDescriptors.filter((f) => f.designType === "string").map((f) => f.physicalName));
|
|
765
838
|
await this.syncIndexesWithDiff({
|
|
766
|
-
listExisting: async () =>
|
|
767
|
-
|
|
839
|
+
listExisting: async () => {
|
|
840
|
+
return (await this._exec().all(`SELECT INDEX_NAME AS name,
|
|
841
|
+
GROUP_CONCAT(COLUMN_NAME ORDER BY SEQ_IN_INDEX SEPARATOR ',') AS columns
|
|
842
|
+
FROM INFORMATION_SCHEMA.STATISTICS
|
|
843
|
+
WHERE TABLE_NAME = ? AND TABLE_SCHEMA = COALESCE(?, DATABASE())
|
|
844
|
+
GROUP BY INDEX_NAME`, [tableName, schema])).map((r) => ({
|
|
845
|
+
name: r.name,
|
|
846
|
+
columns: r.columns ? r.columns.split(",") : void 0
|
|
847
|
+
}));
|
|
848
|
+
},
|
|
768
849
|
createIndex: async (index) => {
|
|
850
|
+
if (index.type === "geo") {
|
|
851
|
+
const fieldMeta = this._table.fieldDescriptors.find((f) => f.physicalName === index.fields[0]?.name);
|
|
852
|
+
if (fieldMeta?.optional) {
|
|
853
|
+
this.logger.warn(`[mysql] geo index "${index.name}" skipped — SPATIAL indexes require a NOT NULL column; make "${fieldMeta.path}" required to index it`);
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
const sql = `CREATE SPATIAL INDEX ${qi(index.key)} ON ${quoteTableName(this.resolveTableName())} (${qi(index.fields[0].name)})`;
|
|
857
|
+
this._log(sql);
|
|
858
|
+
await this._exec().exec(sql);
|
|
859
|
+
return;
|
|
860
|
+
}
|
|
769
861
|
const unique = index.type === "unique" ? "UNIQUE " : "";
|
|
770
862
|
const fulltext = index.type === "fulltext" ? "FULLTEXT " : "";
|
|
771
863
|
const isFulltext = index.type === "fulltext";
|
|
@@ -780,10 +872,6 @@ var MysqlAdapter = class MysqlAdapter extends _atscript_db.BaseDbAdapter {
|
|
|
780
872
|
const sql = `DROP INDEX ${qi(name)} ON ${quoteTableName(this.resolveTableName())}`;
|
|
781
873
|
this._log(sql);
|
|
782
874
|
await this._exec().exec(sql);
|
|
783
|
-
},
|
|
784
|
-
warnUnsupportedTypes: {
|
|
785
|
-
adapter: "mysql",
|
|
786
|
-
types: ["geo"]
|
|
787
875
|
}
|
|
788
876
|
});
|
|
789
877
|
}
|
|
@@ -1010,6 +1098,65 @@ var MysqlAdapter = class MysqlAdapter extends _atscript_db.BaseDbAdapter {
|
|
|
1010
1098
|
if (queryThreshold !== void 0) return queryThreshold;
|
|
1011
1099
|
return this._vectorThresholds.get(indexName);
|
|
1012
1100
|
}
|
|
1101
|
+
/** Native POINT SRID 4326 + ST_Distance_Sphere — available on MySQL 8.0+. */
|
|
1102
|
+
isGeoSearchable() {
|
|
1103
|
+
return true;
|
|
1104
|
+
}
|
|
1105
|
+
async geoSearch(point, query, indexName) {
|
|
1106
|
+
const { sql, params } = this._buildGeoSearchSelect(this._prepareGeoSearch(point, query, indexName));
|
|
1107
|
+
this._log(sql, params);
|
|
1108
|
+
return (await this._exec().all(sql, params)).map((row) => (0, _atscript_db_sql_tools.renameGeoDistance)(row));
|
|
1109
|
+
}
|
|
1110
|
+
async geoSearchWithCount(point, query, indexName) {
|
|
1111
|
+
const ctx = this._prepareGeoSearch(point, query, indexName);
|
|
1112
|
+
const { sql, params } = this._buildGeoSearchSelect(ctx);
|
|
1113
|
+
const countFrag = (0, _atscript_db_sql_tools.buildGeoSearchCount)(mysqlDialect, ctx.tableName, ctx.where, ctx.dist, ctx.window);
|
|
1114
|
+
this._log(sql, params);
|
|
1115
|
+
this._log(countFrag.sql, countFrag.params);
|
|
1116
|
+
const [rows, countRow] = await Promise.all([this._exec().all(sql, params), this._exec().get(countFrag.sql, countFrag.params)]);
|
|
1117
|
+
return {
|
|
1118
|
+
data: rows.map((row) => (0, _atscript_db_sql_tools.renameGeoDistance)(row)),
|
|
1119
|
+
count: Number(countRow?.cnt ?? 0)
|
|
1120
|
+
};
|
|
1121
|
+
}
|
|
1122
|
+
/** Resolves the shared parts of a geo search once (column, filter, distance, window). */
|
|
1123
|
+
_prepareGeoSearch(point, query, indexName) {
|
|
1124
|
+
const column = this._resolveGeoColumn(indexName);
|
|
1125
|
+
const controls = query.controls ?? {};
|
|
1126
|
+
return {
|
|
1127
|
+
tableName: this.resolveTableName(),
|
|
1128
|
+
where: buildWhere(query.filter),
|
|
1129
|
+
dist: mysqlGeoDistanceExpr(qi(column), point),
|
|
1130
|
+
window: (0, _atscript_db_sql_tools.geoWindowFromControls)(controls),
|
|
1131
|
+
controls
|
|
1132
|
+
};
|
|
1133
|
+
}
|
|
1134
|
+
_buildGeoSearchSelect(ctx) {
|
|
1135
|
+
return (0, _atscript_db_sql_tools.buildGeoSearchSelect)(mysqlDialect, ctx.tableName, ctx.where, ctx.dist, ctx.window, {
|
|
1136
|
+
$limit: ctx.controls.$limit,
|
|
1137
|
+
$skip: ctx.controls.$skip
|
|
1138
|
+
});
|
|
1139
|
+
}
|
|
1140
|
+
/**
|
|
1141
|
+
* Migrates a v1 JSON `[lng, lat]` column to native `POINT SRID 4326` via a
|
|
1142
|
+
* temp column (MySQL has no JSON→geometry cast for MODIFY COLUMN).
|
|
1143
|
+
*/
|
|
1144
|
+
async _migrateJsonColumnToPoint(tableName, field) {
|
|
1145
|
+
const col = qi(field.physicalName);
|
|
1146
|
+
const tmp = qi(`${field.physicalName}__geo_mig`);
|
|
1147
|
+
const quotedTable = quoteTableName(tableName);
|
|
1148
|
+
const steps = [
|
|
1149
|
+
`ALTER TABLE ${quotedTable} ADD COLUMN ${tmp} POINT SRID 4326 NULL`,
|
|
1150
|
+
`UPDATE ${quotedTable} SET ${tmp} = ST_SRID(POINT(CAST(${col}->>'$[0]' AS DOUBLE), CAST(${col}->>'$[1]' AS DOUBLE)), 4326) WHERE ${col} IS NOT NULL`,
|
|
1151
|
+
`ALTER TABLE ${quotedTable} DROP COLUMN ${col}`,
|
|
1152
|
+
`ALTER TABLE ${quotedTable} RENAME COLUMN ${tmp} TO ${col}`,
|
|
1153
|
+
...field.optional || field.isPrimaryKey ? [] : [`ALTER TABLE ${quotedTable} MODIFY COLUMN ${col} POINT SRID 4326 NOT NULL`]
|
|
1154
|
+
];
|
|
1155
|
+
for (const ddl of steps) {
|
|
1156
|
+
this._log(ddl);
|
|
1157
|
+
await this._exec().exec(ddl);
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1013
1160
|
};
|
|
1014
1161
|
/**
|
|
1015
1162
|
* Normalizes MySQL INFORMATION_SCHEMA.COLUMNS.COLUMN_DEFAULT values
|
|
@@ -1194,6 +1341,5 @@ function createAdapter(uri, options) {
|
|
|
1194
1341
|
//#endregion
|
|
1195
1342
|
exports.Mysql2Driver = Mysql2Driver;
|
|
1196
1343
|
exports.MysqlAdapter = MysqlAdapter;
|
|
1197
|
-
exports.MysqlPlugin = require_plugin.MysqlPlugin;
|
|
1198
1344
|
exports.buildWhere = buildWhere;
|
|
1199
1345
|
exports.createAdapter = createAdapter;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { t as MysqlPlugin } from "./index-BLQr6loH.cjs";
|
|
2
1
|
import { BaseDbAdapter, DbQuery, DbSpace, FilterExpr, TColumnDiff, TDbDefaultFn, TDbDeleteResult, TDbFieldMeta, TDbInsertManyResult, TDbInsertResult, TDbUpdateResult, TExistingColumn, TExistingTableOption, TFieldOps, TSearchIndexInfo, TSyncColumnResult, TTableOptionDiff, TValueFormatterPair } from "@atscript/db";
|
|
3
2
|
import { TMetadataMap } from "@atscript/typescript/utils";
|
|
4
3
|
import { FilterExpr as FilterExpr$1 } from "@uniqu/core";
|
|
@@ -158,6 +157,7 @@ declare class MysqlAdapter extends BaseDbAdapter {
|
|
|
158
157
|
replaceMany(filter: FilterExpr, data: Record<string, unknown>): Promise<TDbUpdateResult>;
|
|
159
158
|
deleteOne(filter: FilterExpr): Promise<TDbDeleteResult>;
|
|
160
159
|
deleteMany(filter: FilterExpr): Promise<TDbDeleteResult>;
|
|
160
|
+
prepareTypeMapper(): Promise<void>;
|
|
161
161
|
ensureTable(): Promise<void>;
|
|
162
162
|
private _ensureView;
|
|
163
163
|
getExistingColumns(): Promise<TExistingColumn[]>;
|
|
@@ -166,6 +166,7 @@ declare class MysqlAdapter extends BaseDbAdapter {
|
|
|
166
166
|
recreateTable(): Promise<void>;
|
|
167
167
|
dropTable(): Promise<void>;
|
|
168
168
|
dropColumns(columns: string[]): Promise<void>;
|
|
169
|
+
dropIndexesForColumns(columns: string[]): Promise<void>;
|
|
169
170
|
dropTableByName(tableName: string): Promise<void>;
|
|
170
171
|
dropViewByName(viewName: string): Promise<void>;
|
|
171
172
|
renameTable(oldName: string): Promise<void>;
|
|
@@ -201,6 +202,21 @@ declare class MysqlAdapter extends BaseDbAdapter {
|
|
|
201
202
|
private _buildVectorSearchCountQuery;
|
|
202
203
|
/** Resolves threshold: query-time $threshold > schema-level @db.search.vector.threshold. */
|
|
203
204
|
private _resolveVectorThreshold;
|
|
205
|
+
/** Native POINT SRID 4326 + ST_Distance_Sphere — available on MySQL 8.0+. */
|
|
206
|
+
isGeoSearchable(): boolean;
|
|
207
|
+
geoSearch(point: [number, number], query: DbQuery, indexName?: string): Promise<Array<Record<string, unknown>>>;
|
|
208
|
+
geoSearchWithCount(point: [number, number], query: DbQuery, indexName?: string): Promise<{
|
|
209
|
+
data: Array<Record<string, unknown>>;
|
|
210
|
+
count: number;
|
|
211
|
+
}>;
|
|
212
|
+
/** Resolves the shared parts of a geo search once (column, filter, distance, window). */
|
|
213
|
+
private _prepareGeoSearch;
|
|
214
|
+
private _buildGeoSearchSelect;
|
|
215
|
+
/**
|
|
216
|
+
* Migrates a v1 JSON `[lng, lat]` column to native `POINT SRID 4326` via a
|
|
217
|
+
* temp column (MySQL has no JSON→geometry cast for MODIFY COLUMN).
|
|
218
|
+
*/
|
|
219
|
+
private _migrateJsonColumnToPoint;
|
|
204
220
|
}
|
|
205
221
|
//#endregion
|
|
206
222
|
//#region src/mysql2-driver.d.ts
|
|
@@ -268,4 +284,4 @@ declare function buildWhere(filter: FilterExpr$1): TSqlFragment$1;
|
|
|
268
284
|
*/
|
|
269
285
|
declare function createAdapter(uri: string, options?: Record<string, unknown>): DbSpace;
|
|
270
286
|
//#endregion
|
|
271
|
-
export { Mysql2Driver, MysqlAdapter,
|
|
287
|
+
export { Mysql2Driver, MysqlAdapter, type TMysqlConnection, type TMysqlDriver, type TMysqlRunResult, type TSqlFragment, buildWhere, createAdapter };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { t as MysqlPlugin } from "./index-BLQr6loH.mjs";
|
|
2
1
|
import { BaseDbAdapter, DbQuery, DbSpace, FilterExpr, TColumnDiff, TDbDefaultFn, TDbDeleteResult, TDbFieldMeta, TDbInsertManyResult, TDbInsertResult, TDbUpdateResult, TExistingColumn, TExistingTableOption, TFieldOps, TSearchIndexInfo, TSyncColumnResult, TTableOptionDiff, TValueFormatterPair } from "@atscript/db";
|
|
3
2
|
import { TSqlFragment, TSqlFragment as TSqlFragment$1 } from "@atscript/db-sql-tools";
|
|
4
3
|
import { TMetadataMap } from "@atscript/typescript/utils";
|
|
@@ -158,6 +157,7 @@ declare class MysqlAdapter extends BaseDbAdapter {
|
|
|
158
157
|
replaceMany(filter: FilterExpr, data: Record<string, unknown>): Promise<TDbUpdateResult>;
|
|
159
158
|
deleteOne(filter: FilterExpr): Promise<TDbDeleteResult>;
|
|
160
159
|
deleteMany(filter: FilterExpr): Promise<TDbDeleteResult>;
|
|
160
|
+
prepareTypeMapper(): Promise<void>;
|
|
161
161
|
ensureTable(): Promise<void>;
|
|
162
162
|
private _ensureView;
|
|
163
163
|
getExistingColumns(): Promise<TExistingColumn[]>;
|
|
@@ -166,6 +166,7 @@ declare class MysqlAdapter extends BaseDbAdapter {
|
|
|
166
166
|
recreateTable(): Promise<void>;
|
|
167
167
|
dropTable(): Promise<void>;
|
|
168
168
|
dropColumns(columns: string[]): Promise<void>;
|
|
169
|
+
dropIndexesForColumns(columns: string[]): Promise<void>;
|
|
169
170
|
dropTableByName(tableName: string): Promise<void>;
|
|
170
171
|
dropViewByName(viewName: string): Promise<void>;
|
|
171
172
|
renameTable(oldName: string): Promise<void>;
|
|
@@ -201,6 +202,21 @@ declare class MysqlAdapter extends BaseDbAdapter {
|
|
|
201
202
|
private _buildVectorSearchCountQuery;
|
|
202
203
|
/** Resolves threshold: query-time $threshold > schema-level @db.search.vector.threshold. */
|
|
203
204
|
private _resolveVectorThreshold;
|
|
205
|
+
/** Native POINT SRID 4326 + ST_Distance_Sphere — available on MySQL 8.0+. */
|
|
206
|
+
isGeoSearchable(): boolean;
|
|
207
|
+
geoSearch(point: [number, number], query: DbQuery, indexName?: string): Promise<Array<Record<string, unknown>>>;
|
|
208
|
+
geoSearchWithCount(point: [number, number], query: DbQuery, indexName?: string): Promise<{
|
|
209
|
+
data: Array<Record<string, unknown>>;
|
|
210
|
+
count: number;
|
|
211
|
+
}>;
|
|
212
|
+
/** Resolves the shared parts of a geo search once (column, filter, distance, window). */
|
|
213
|
+
private _prepareGeoSearch;
|
|
214
|
+
private _buildGeoSearchSelect;
|
|
215
|
+
/**
|
|
216
|
+
* Migrates a v1 JSON `[lng, lat]` column to native `POINT SRID 4326` via a
|
|
217
|
+
* temp column (MySQL has no JSON→geometry cast for MODIFY COLUMN).
|
|
218
|
+
*/
|
|
219
|
+
private _migrateJsonColumnToPoint;
|
|
204
220
|
}
|
|
205
221
|
//#endregion
|
|
206
222
|
//#region src/mysql2-driver.d.ts
|
|
@@ -268,4 +284,4 @@ declare function buildWhere(filter: FilterExpr$1): TSqlFragment$1;
|
|
|
268
284
|
*/
|
|
269
285
|
declare function createAdapter(uri: string, options?: Record<string, unknown>): DbSpace;
|
|
270
286
|
//#endregion
|
|
271
|
-
export { Mysql2Driver, MysqlAdapter,
|
|
287
|
+
export { Mysql2Driver, MysqlAdapter, type TMysqlConnection, type TMysqlDriver, type TMysqlRunResult, type TSqlFragment, buildWhere, createAdapter };
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { t as MysqlPlugin } from "./plugin-CPJY5K_I.mjs";
|
|
2
1
|
import { AtscriptDbView, BaseDbAdapter, DbError, DbSpace } from "@atscript/db";
|
|
3
|
-
import { buildAggregateCount, buildAggregateSelect, buildCreateView, buildDelete, buildInsert, buildSelect, buildUpdate, buildWhere as buildWhere$1, defaultValueForType, defaultValueToSqlLiteral, parseRegexString, refActionToSql, toSqlValue } from "@atscript/db-sql-tools";
|
|
2
|
+
import { buildAggregateCount, buildAggregateSelect, buildCreateView, buildDelete, buildGeoSearchCount, buildGeoSearchSelect, buildInsert, buildSelect, buildUpdate, buildWhere as buildWhere$1, defaultValueForType, defaultValueToSqlLiteral, geoWindowFromControls, normalizeGeoPointValue, parseRegexString, refActionToSql, renameGeoDistance, toSqlValue } from "@atscript/db-sql-tools";
|
|
4
3
|
//#region src/sql-builder.ts
|
|
5
4
|
/** Escapes a MySQL identifier by doubling backticks. */
|
|
6
5
|
function esc(name) {
|
|
@@ -39,9 +38,53 @@ const mysqlDialect = {
|
|
|
39
38
|
params: [pattern]
|
|
40
39
|
};
|
|
41
40
|
},
|
|
41
|
+
geoWithin(quotedCol, circle) {
|
|
42
|
+
const dist = mysqlGeoDistanceExpr(quotedCol, circle.center);
|
|
43
|
+
return {
|
|
44
|
+
sql: `${dist.sql} <= ?`,
|
|
45
|
+
params: [...dist.params, circle.radius]
|
|
46
|
+
};
|
|
47
|
+
},
|
|
42
48
|
createViewPrefix: "CREATE OR REPLACE VIEW"
|
|
43
49
|
};
|
|
44
50
|
/**
|
|
51
|
+
* Spherical distance in meters from a POINT column to a query point.
|
|
52
|
+
* Used as the `distExpr` of the shared geo search builder.
|
|
53
|
+
*/
|
|
54
|
+
function mysqlGeoDistanceExpr(quotedCol, point) {
|
|
55
|
+
return {
|
|
56
|
+
sql: `ST_Distance_Sphere(${quotedCol}, ST_SRID(POINT(?, ?), 4326))`,
|
|
57
|
+
params: [point[0], point[1]]
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Encodes a `[lng, lat]` tuple in MySQL's internal geometry format
|
|
62
|
+
* (4-byte SRID LE + WKB point) — geometry columns accept it directly as a
|
|
63
|
+
* binary parameter, bypassing the WKT/WKB axis-order pitfalls entirely.
|
|
64
|
+
*/
|
|
65
|
+
function geoPointToMysqlInternal(point) {
|
|
66
|
+
const buf = Buffer.alloc(25);
|
|
67
|
+
buf.writeUInt32LE(4326, 0);
|
|
68
|
+
buf.writeUInt8(1, 4);
|
|
69
|
+
buf.writeUInt32LE(1, 5);
|
|
70
|
+
buf.writeDoubleLE(point[0], 9);
|
|
71
|
+
buf.writeDoubleLE(point[1], 17);
|
|
72
|
+
return buf;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Decodes a geo read value back to `[lng, lat]`. The mysql2 driver parses
|
|
76
|
+
* POINT columns to `{x, y}` objects (internal order: x=lng, y=lat); raw
|
|
77
|
+
* internal-format buffers are handled for drivers that don't.
|
|
78
|
+
*/
|
|
79
|
+
function mysqlGeoValueToPoint(value) {
|
|
80
|
+
if (typeof value === "object" && value !== null && typeof value.x === "number" && typeof value.y === "number") return [value.x, value.y];
|
|
81
|
+
if (value instanceof Uint8Array && value.length >= 25) {
|
|
82
|
+
const buf = Buffer.isBuffer(value) ? value : Buffer.from(value);
|
|
83
|
+
const littleEndian = buf.readUInt8(4) === 1;
|
|
84
|
+
if (((littleEndian ? buf.readUInt32LE(5) : buf.readUInt32BE(5)) & 255) === 1) return [littleEndian ? buf.readDoubleLE(9) : buf.readDoubleBE(9), littleEndian ? buf.readDoubleLE(17) : buf.readDoubleBE(17)];
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
45
88
|
* Builds an INSERT statement.
|
|
46
89
|
*/
|
|
47
90
|
function buildInsert$1(table, data) {
|
|
@@ -121,6 +164,7 @@ function mysqlTypeFromField(field) {
|
|
|
121
164
|
const metadata = field.type?.metadata;
|
|
122
165
|
const mysqlTypeOverride = metadata?.get("db.mysql.type");
|
|
123
166
|
if (mysqlTypeOverride) return mysqlTypeOverride;
|
|
167
|
+
if (field.isGeoPoint && !field.encrypted) return "POINT SRID 4326";
|
|
124
168
|
const unsigned = metadata?.has("db.mysql.unsigned") ?? false;
|
|
125
169
|
const precision = metadata?.get("db.column.precision");
|
|
126
170
|
switch (field.designType) {
|
|
@@ -409,6 +453,13 @@ var MysqlAdapter = class MysqlAdapter extends BaseDbAdapter {
|
|
|
409
453
|
toStorage: (value) => typeof value === "number" ? epochMsToUtcDatetime(value) : value,
|
|
410
454
|
fromStorage: utcDatetimeToEpochMs
|
|
411
455
|
};
|
|
456
|
+
if (field.isGeoPoint && !field.encrypted) return {
|
|
457
|
+
toStorage: (value) => {
|
|
458
|
+
const point = normalizeGeoPointValue(value);
|
|
459
|
+
return point ? geoPointToMysqlInternal(point) : value;
|
|
460
|
+
},
|
|
461
|
+
fromStorage: (value) => mysqlGeoValueToPoint(value) ?? value
|
|
462
|
+
};
|
|
412
463
|
}
|
|
413
464
|
/**
|
|
414
465
|
* Wraps an async write operation to catch MySQL constraint errors
|
|
@@ -578,8 +629,11 @@ var MysqlAdapter = class MysqlAdapter extends BaseDbAdapter {
|
|
|
578
629
|
this._log(sql, params);
|
|
579
630
|
return { deletedCount: (await this._wrapConstraintError(() => this._exec().run(sql, params))).affectedRows };
|
|
580
631
|
}
|
|
581
|
-
async
|
|
632
|
+
async prepareTypeMapper() {
|
|
582
633
|
if (this._supportsVector === void 0 && this._vectorFields.size > 0) await this._detectVectorSupport();
|
|
634
|
+
}
|
|
635
|
+
async ensureTable() {
|
|
636
|
+
await this.prepareTypeMapper();
|
|
583
637
|
if (this._table instanceof AtscriptDbView) return this._ensureView();
|
|
584
638
|
const sql = buildCreateTable(this.resolveTableName(), this._table.fieldDescriptors, this._table.foreignKeys, {
|
|
585
639
|
engine: this._engine,
|
|
@@ -603,12 +657,12 @@ var MysqlAdapter = class MysqlAdapter extends BaseDbAdapter {
|
|
|
603
657
|
}
|
|
604
658
|
async getExistingColumnsForTable(tableName) {
|
|
605
659
|
const schema = this._schema;
|
|
606
|
-
return (await this._exec().all(`SELECT COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_KEY, COLUMN_DEFAULT
|
|
660
|
+
return (await this._exec().all(`SELECT COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_KEY, COLUMN_DEFAULT, SRS_ID
|
|
607
661
|
FROM INFORMATION_SCHEMA.COLUMNS
|
|
608
662
|
WHERE TABLE_NAME = ? AND TABLE_SCHEMA = COALESCE(?, DATABASE())
|
|
609
663
|
ORDER BY ORDINAL_POSITION`, [tableName, schema])).map((r) => ({
|
|
610
664
|
name: r.COLUMN_NAME,
|
|
611
|
-
type: r.COLUMN_TYPE.toUpperCase()
|
|
665
|
+
type: r.SRS_ID == null ? r.COLUMN_TYPE.toUpperCase() : `${r.COLUMN_TYPE.toUpperCase()} SRID ${r.SRS_ID}`,
|
|
612
666
|
notnull: r.IS_NULLABLE === "NO",
|
|
613
667
|
pk: r.COLUMN_KEY === "PRI",
|
|
614
668
|
dflt_value: normalizeMysqlDefault(r.COLUMN_DEFAULT)
|
|
@@ -629,7 +683,7 @@ var MysqlAdapter = class MysqlAdapter extends BaseDbAdapter {
|
|
|
629
683
|
let ddl = `ALTER TABLE ${quoteTableName(tableName)} ADD COLUMN ${qi(field.physicalName)} ${sqlType}`;
|
|
630
684
|
if (!field.optional && !field.isPrimaryKey) ddl += " NOT NULL";
|
|
631
685
|
if (field.defaultValue?.kind === "value") ddl += ` DEFAULT ${defaultValueToSqlLiteral(field.designType, field.defaultValue.value)}`;
|
|
632
|
-
else if (!field.optional && !field.isPrimaryKey) ddl += ` DEFAULT ${defaultValueForType(field.designType)}`;
|
|
686
|
+
else if (!field.optional && !field.isPrimaryKey) ddl += field.isGeoPoint && !field.encrypted ? ` DEFAULT (ST_SRID(POINT(0, 0), 4326))` : ` DEFAULT ${defaultValueForType(field.designType)}`;
|
|
633
687
|
if (field.collate) {
|
|
634
688
|
const nativeCollate = field.type?.metadata?.get("db.mysql.collate");
|
|
635
689
|
ddl += ` COLLATE ${nativeCollate ?? collationToMysql(field.collate)}`;
|
|
@@ -640,6 +694,10 @@ var MysqlAdapter = class MysqlAdapter extends BaseDbAdapter {
|
|
|
640
694
|
}
|
|
641
695
|
for (const { field } of diff.typeChanged ?? []) {
|
|
642
696
|
const sqlType = this.typeMapper(field);
|
|
697
|
+
if (field.isGeoPoint && !field.encrypted && sqlType.startsWith("POINT")) {
|
|
698
|
+
await this._migrateJsonColumnToPoint(tableName, field);
|
|
699
|
+
continue;
|
|
700
|
+
}
|
|
643
701
|
let ddl = `ALTER TABLE ${quoteTableName(tableName)} MODIFY COLUMN ${qi(field.physicalName)} ${sqlType}`;
|
|
644
702
|
if (!field.optional && !field.isPrimaryKey) ddl += " NOT NULL";
|
|
645
703
|
this._log(ddl);
|
|
@@ -726,6 +784,21 @@ var MysqlAdapter = class MysqlAdapter extends BaseDbAdapter {
|
|
|
726
784
|
this._log(ddl);
|
|
727
785
|
await this._exec().exec(ddl);
|
|
728
786
|
}
|
|
787
|
+
async dropIndexesForColumns(columns) {
|
|
788
|
+
const placeholders = columns.map(() => "?").join(", ");
|
|
789
|
+
const rows = await this._exec().all(`SELECT DISTINCT INDEX_NAME AS name FROM INFORMATION_SCHEMA.STATISTICS
|
|
790
|
+
WHERE TABLE_NAME = ? AND TABLE_SCHEMA = COALESCE(?, DATABASE())
|
|
791
|
+
AND INDEX_NAME LIKE 'atscript\\_\\_%' AND COLUMN_NAME IN (${placeholders})`, [
|
|
792
|
+
this._table.tableName,
|
|
793
|
+
this._schema,
|
|
794
|
+
...columns
|
|
795
|
+
]);
|
|
796
|
+
for (const row of rows) {
|
|
797
|
+
const sql = `DROP INDEX ${qi(row.name)} ON ${quoteTableName(this.resolveTableName())}`;
|
|
798
|
+
this._log(sql);
|
|
799
|
+
await this._exec().exec(sql);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
729
802
|
async dropTableByName(tableName) {
|
|
730
803
|
const ddl = `DROP TABLE IF EXISTS ${quoteTableName(tableName)}`;
|
|
731
804
|
this._log(ddl);
|
|
@@ -762,9 +835,28 @@ var MysqlAdapter = class MysqlAdapter extends BaseDbAdapter {
|
|
|
762
835
|
const schema = this._schema;
|
|
763
836
|
const stringFields = new Set(this._table.fieldDescriptors.filter((f) => f.designType === "string").map((f) => f.physicalName));
|
|
764
837
|
await this.syncIndexesWithDiff({
|
|
765
|
-
listExisting: async () =>
|
|
766
|
-
|
|
838
|
+
listExisting: async () => {
|
|
839
|
+
return (await this._exec().all(`SELECT INDEX_NAME AS name,
|
|
840
|
+
GROUP_CONCAT(COLUMN_NAME ORDER BY SEQ_IN_INDEX SEPARATOR ',') AS columns
|
|
841
|
+
FROM INFORMATION_SCHEMA.STATISTICS
|
|
842
|
+
WHERE TABLE_NAME = ? AND TABLE_SCHEMA = COALESCE(?, DATABASE())
|
|
843
|
+
GROUP BY INDEX_NAME`, [tableName, schema])).map((r) => ({
|
|
844
|
+
name: r.name,
|
|
845
|
+
columns: r.columns ? r.columns.split(",") : void 0
|
|
846
|
+
}));
|
|
847
|
+
},
|
|
767
848
|
createIndex: async (index) => {
|
|
849
|
+
if (index.type === "geo") {
|
|
850
|
+
const fieldMeta = this._table.fieldDescriptors.find((f) => f.physicalName === index.fields[0]?.name);
|
|
851
|
+
if (fieldMeta?.optional) {
|
|
852
|
+
this.logger.warn(`[mysql] geo index "${index.name}" skipped — SPATIAL indexes require a NOT NULL column; make "${fieldMeta.path}" required to index it`);
|
|
853
|
+
return;
|
|
854
|
+
}
|
|
855
|
+
const sql = `CREATE SPATIAL INDEX ${qi(index.key)} ON ${quoteTableName(this.resolveTableName())} (${qi(index.fields[0].name)})`;
|
|
856
|
+
this._log(sql);
|
|
857
|
+
await this._exec().exec(sql);
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
768
860
|
const unique = index.type === "unique" ? "UNIQUE " : "";
|
|
769
861
|
const fulltext = index.type === "fulltext" ? "FULLTEXT " : "";
|
|
770
862
|
const isFulltext = index.type === "fulltext";
|
|
@@ -779,10 +871,6 @@ var MysqlAdapter = class MysqlAdapter extends BaseDbAdapter {
|
|
|
779
871
|
const sql = `DROP INDEX ${qi(name)} ON ${quoteTableName(this.resolveTableName())}`;
|
|
780
872
|
this._log(sql);
|
|
781
873
|
await this._exec().exec(sql);
|
|
782
|
-
},
|
|
783
|
-
warnUnsupportedTypes: {
|
|
784
|
-
adapter: "mysql",
|
|
785
|
-
types: ["geo"]
|
|
786
874
|
}
|
|
787
875
|
});
|
|
788
876
|
}
|
|
@@ -1009,6 +1097,65 @@ var MysqlAdapter = class MysqlAdapter extends BaseDbAdapter {
|
|
|
1009
1097
|
if (queryThreshold !== void 0) return queryThreshold;
|
|
1010
1098
|
return this._vectorThresholds.get(indexName);
|
|
1011
1099
|
}
|
|
1100
|
+
/** Native POINT SRID 4326 + ST_Distance_Sphere — available on MySQL 8.0+. */
|
|
1101
|
+
isGeoSearchable() {
|
|
1102
|
+
return true;
|
|
1103
|
+
}
|
|
1104
|
+
async geoSearch(point, query, indexName) {
|
|
1105
|
+
const { sql, params } = this._buildGeoSearchSelect(this._prepareGeoSearch(point, query, indexName));
|
|
1106
|
+
this._log(sql, params);
|
|
1107
|
+
return (await this._exec().all(sql, params)).map((row) => renameGeoDistance(row));
|
|
1108
|
+
}
|
|
1109
|
+
async geoSearchWithCount(point, query, indexName) {
|
|
1110
|
+
const ctx = this._prepareGeoSearch(point, query, indexName);
|
|
1111
|
+
const { sql, params } = this._buildGeoSearchSelect(ctx);
|
|
1112
|
+
const countFrag = buildGeoSearchCount(mysqlDialect, ctx.tableName, ctx.where, ctx.dist, ctx.window);
|
|
1113
|
+
this._log(sql, params);
|
|
1114
|
+
this._log(countFrag.sql, countFrag.params);
|
|
1115
|
+
const [rows, countRow] = await Promise.all([this._exec().all(sql, params), this._exec().get(countFrag.sql, countFrag.params)]);
|
|
1116
|
+
return {
|
|
1117
|
+
data: rows.map((row) => renameGeoDistance(row)),
|
|
1118
|
+
count: Number(countRow?.cnt ?? 0)
|
|
1119
|
+
};
|
|
1120
|
+
}
|
|
1121
|
+
/** Resolves the shared parts of a geo search once (column, filter, distance, window). */
|
|
1122
|
+
_prepareGeoSearch(point, query, indexName) {
|
|
1123
|
+
const column = this._resolveGeoColumn(indexName);
|
|
1124
|
+
const controls = query.controls ?? {};
|
|
1125
|
+
return {
|
|
1126
|
+
tableName: this.resolveTableName(),
|
|
1127
|
+
where: buildWhere(query.filter),
|
|
1128
|
+
dist: mysqlGeoDistanceExpr(qi(column), point),
|
|
1129
|
+
window: geoWindowFromControls(controls),
|
|
1130
|
+
controls
|
|
1131
|
+
};
|
|
1132
|
+
}
|
|
1133
|
+
_buildGeoSearchSelect(ctx) {
|
|
1134
|
+
return buildGeoSearchSelect(mysqlDialect, ctx.tableName, ctx.where, ctx.dist, ctx.window, {
|
|
1135
|
+
$limit: ctx.controls.$limit,
|
|
1136
|
+
$skip: ctx.controls.$skip
|
|
1137
|
+
});
|
|
1138
|
+
}
|
|
1139
|
+
/**
|
|
1140
|
+
* Migrates a v1 JSON `[lng, lat]` column to native `POINT SRID 4326` via a
|
|
1141
|
+
* temp column (MySQL has no JSON→geometry cast for MODIFY COLUMN).
|
|
1142
|
+
*/
|
|
1143
|
+
async _migrateJsonColumnToPoint(tableName, field) {
|
|
1144
|
+
const col = qi(field.physicalName);
|
|
1145
|
+
const tmp = qi(`${field.physicalName}__geo_mig`);
|
|
1146
|
+
const quotedTable = quoteTableName(tableName);
|
|
1147
|
+
const steps = [
|
|
1148
|
+
`ALTER TABLE ${quotedTable} ADD COLUMN ${tmp} POINT SRID 4326 NULL`,
|
|
1149
|
+
`UPDATE ${quotedTable} SET ${tmp} = ST_SRID(POINT(CAST(${col}->>'$[0]' AS DOUBLE), CAST(${col}->>'$[1]' AS DOUBLE)), 4326) WHERE ${col} IS NOT NULL`,
|
|
1150
|
+
`ALTER TABLE ${quotedTable} DROP COLUMN ${col}`,
|
|
1151
|
+
`ALTER TABLE ${quotedTable} RENAME COLUMN ${tmp} TO ${col}`,
|
|
1152
|
+
...field.optional || field.isPrimaryKey ? [] : [`ALTER TABLE ${quotedTable} MODIFY COLUMN ${col} POINT SRID 4326 NOT NULL`]
|
|
1153
|
+
];
|
|
1154
|
+
for (const ddl of steps) {
|
|
1155
|
+
this._log(ddl);
|
|
1156
|
+
await this._exec().exec(ddl);
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1012
1159
|
};
|
|
1013
1160
|
/**
|
|
1014
1161
|
* Normalizes MySQL INFORMATION_SCHEMA.COLUMNS.COLUMN_DEFAULT values
|
|
@@ -1191,4 +1338,4 @@ function createAdapter(uri, options) {
|
|
|
1191
1338
|
return new DbSpace(() => new MysqlAdapter(driver));
|
|
1192
1339
|
}
|
|
1193
1340
|
//#endregion
|
|
1194
|
-
export { Mysql2Driver, MysqlAdapter,
|
|
1341
|
+
export { Mysql2Driver, MysqlAdapter, buildWhere, createAdapter };
|
package/dist/plugin.cjs
CHANGED
|
@@ -1,3 +1,94 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
let _atscript_core = require("@atscript/core");
|
|
3
|
+
//#region src/plugin/annotations.ts
|
|
4
|
+
/**
|
|
5
|
+
* MySQL-specific annotations.
|
|
6
|
+
*
|
|
7
|
+
* Merged into the global config under `{ db: { mysql: ... } }` so they
|
|
8
|
+
* live alongside core's `@db.table`, `@db.index.*`, etc.
|
|
9
|
+
*
|
|
10
|
+
* These annotations opt-in to MySQL-specific behavior. Files using only
|
|
11
|
+
* portable `@db.*` annotations remain adapter-agnostic.
|
|
12
|
+
*/
|
|
13
|
+
const annotations = {
|
|
14
|
+
engine: new _atscript_core.AnnotationSpec({
|
|
15
|
+
description: "Specifies the MySQL storage engine.\n\n**Default:** `\"InnoDB\"`\n\n```atscript\n@db.mysql.engine \"MyISAM\"\nexport interface Logs { ... }\n```",
|
|
16
|
+
nodeType: ["interface"],
|
|
17
|
+
multiple: false,
|
|
18
|
+
argument: {
|
|
19
|
+
name: "engine",
|
|
20
|
+
type: "string",
|
|
21
|
+
values: [
|
|
22
|
+
"InnoDB",
|
|
23
|
+
"MyISAM",
|
|
24
|
+
"MEMORY",
|
|
25
|
+
"CSV",
|
|
26
|
+
"ARCHIVE"
|
|
27
|
+
],
|
|
28
|
+
description: "MySQL storage engine name."
|
|
29
|
+
}
|
|
30
|
+
}),
|
|
31
|
+
charset: new _atscript_core.AnnotationSpec({
|
|
32
|
+
description: "Specifies the character set for the table or column.\n\n**Default:** `\"utf8mb4\"`\n\n```atscript\n@db.mysql.charset \"latin1\"\nexport interface Legacy { ... }\n```",
|
|
33
|
+
nodeType: ["interface", "prop"],
|
|
34
|
+
multiple: false,
|
|
35
|
+
argument: {
|
|
36
|
+
name: "charset",
|
|
37
|
+
type: "string",
|
|
38
|
+
values: [
|
|
39
|
+
"utf8mb4",
|
|
40
|
+
"utf8",
|
|
41
|
+
"latin1",
|
|
42
|
+
"ascii",
|
|
43
|
+
"binary"
|
|
44
|
+
],
|
|
45
|
+
description: "MySQL character set name."
|
|
46
|
+
}
|
|
47
|
+
}),
|
|
48
|
+
collate: new _atscript_core.AnnotationSpec({
|
|
49
|
+
description: "Specifies a native MySQL collation (overrides portable `@db.column.collate`).\n\n```atscript\n@db.mysql.collate \"utf8mb4_turkish_ci\"\nname: string\n```",
|
|
50
|
+
nodeType: ["interface", "prop"],
|
|
51
|
+
multiple: false,
|
|
52
|
+
argument: {
|
|
53
|
+
name: "collation",
|
|
54
|
+
type: "string",
|
|
55
|
+
description: "Native MySQL collation name (e.g., \"utf8mb4_turkish_ci\")."
|
|
56
|
+
}
|
|
57
|
+
}),
|
|
58
|
+
unsigned: new _atscript_core.AnnotationSpec({
|
|
59
|
+
description: "Adds the UNSIGNED modifier to an integer column.\n\n```atscript\n@db.mysql.unsigned\nage: number.int\n```",
|
|
60
|
+
nodeType: ["prop"],
|
|
61
|
+
multiple: false
|
|
62
|
+
}),
|
|
63
|
+
type: new _atscript_core.AnnotationSpec({
|
|
64
|
+
description: "Overrides the native MySQL column type.\n\n```atscript\n@db.mysql.type \"MEDIUMTEXT\"\nbio: string\n```",
|
|
65
|
+
nodeType: ["prop"],
|
|
66
|
+
multiple: false,
|
|
67
|
+
argument: {
|
|
68
|
+
name: "type",
|
|
69
|
+
type: "string",
|
|
70
|
+
description: "Native MySQL column type (e.g., \"MEDIUMTEXT\", \"TINYTEXT\")."
|
|
71
|
+
}
|
|
72
|
+
}),
|
|
73
|
+
onUpdate: new _atscript_core.AnnotationSpec({
|
|
74
|
+
description: "Sets the MySQL ON UPDATE clause for a column.\n\n```atscript\n@db.mysql.onUpdate \"CURRENT_TIMESTAMP\"\nupdatedAt: number.timestamp\n```",
|
|
75
|
+
nodeType: ["prop"],
|
|
76
|
+
multiple: false,
|
|
77
|
+
argument: {
|
|
78
|
+
name: "expression",
|
|
79
|
+
type: "string",
|
|
80
|
+
values: ["CURRENT_TIMESTAMP"],
|
|
81
|
+
description: "Expression to evaluate on row update."
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
};
|
|
85
|
+
//#endregion
|
|
86
|
+
//#region src/plugin/index.ts
|
|
87
|
+
const MysqlPlugin = () => ({
|
|
88
|
+
name: "mysql",
|
|
89
|
+
config() {
|
|
90
|
+
return { annotations: { db: { mysql: annotations } } };
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
//#endregion
|
|
94
|
+
exports.MysqlPlugin = MysqlPlugin;
|
package/dist/plugin.d.cts
CHANGED
package/dist/plugin.d.mts
CHANGED
package/dist/plugin.mjs
CHANGED
|
@@ -1,2 +1,93 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AnnotationSpec } from "@atscript/core";
|
|
2
|
+
//#region src/plugin/annotations.ts
|
|
3
|
+
/**
|
|
4
|
+
* MySQL-specific annotations.
|
|
5
|
+
*
|
|
6
|
+
* Merged into the global config under `{ db: { mysql: ... } }` so they
|
|
7
|
+
* live alongside core's `@db.table`, `@db.index.*`, etc.
|
|
8
|
+
*
|
|
9
|
+
* These annotations opt-in to MySQL-specific behavior. Files using only
|
|
10
|
+
* portable `@db.*` annotations remain adapter-agnostic.
|
|
11
|
+
*/
|
|
12
|
+
const annotations = {
|
|
13
|
+
engine: new AnnotationSpec({
|
|
14
|
+
description: "Specifies the MySQL storage engine.\n\n**Default:** `\"InnoDB\"`\n\n```atscript\n@db.mysql.engine \"MyISAM\"\nexport interface Logs { ... }\n```",
|
|
15
|
+
nodeType: ["interface"],
|
|
16
|
+
multiple: false,
|
|
17
|
+
argument: {
|
|
18
|
+
name: "engine",
|
|
19
|
+
type: "string",
|
|
20
|
+
values: [
|
|
21
|
+
"InnoDB",
|
|
22
|
+
"MyISAM",
|
|
23
|
+
"MEMORY",
|
|
24
|
+
"CSV",
|
|
25
|
+
"ARCHIVE"
|
|
26
|
+
],
|
|
27
|
+
description: "MySQL storage engine name."
|
|
28
|
+
}
|
|
29
|
+
}),
|
|
30
|
+
charset: new AnnotationSpec({
|
|
31
|
+
description: "Specifies the character set for the table or column.\n\n**Default:** `\"utf8mb4\"`\n\n```atscript\n@db.mysql.charset \"latin1\"\nexport interface Legacy { ... }\n```",
|
|
32
|
+
nodeType: ["interface", "prop"],
|
|
33
|
+
multiple: false,
|
|
34
|
+
argument: {
|
|
35
|
+
name: "charset",
|
|
36
|
+
type: "string",
|
|
37
|
+
values: [
|
|
38
|
+
"utf8mb4",
|
|
39
|
+
"utf8",
|
|
40
|
+
"latin1",
|
|
41
|
+
"ascii",
|
|
42
|
+
"binary"
|
|
43
|
+
],
|
|
44
|
+
description: "MySQL character set name."
|
|
45
|
+
}
|
|
46
|
+
}),
|
|
47
|
+
collate: new AnnotationSpec({
|
|
48
|
+
description: "Specifies a native MySQL collation (overrides portable `@db.column.collate`).\n\n```atscript\n@db.mysql.collate \"utf8mb4_turkish_ci\"\nname: string\n```",
|
|
49
|
+
nodeType: ["interface", "prop"],
|
|
50
|
+
multiple: false,
|
|
51
|
+
argument: {
|
|
52
|
+
name: "collation",
|
|
53
|
+
type: "string",
|
|
54
|
+
description: "Native MySQL collation name (e.g., \"utf8mb4_turkish_ci\")."
|
|
55
|
+
}
|
|
56
|
+
}),
|
|
57
|
+
unsigned: new AnnotationSpec({
|
|
58
|
+
description: "Adds the UNSIGNED modifier to an integer column.\n\n```atscript\n@db.mysql.unsigned\nage: number.int\n```",
|
|
59
|
+
nodeType: ["prop"],
|
|
60
|
+
multiple: false
|
|
61
|
+
}),
|
|
62
|
+
type: new AnnotationSpec({
|
|
63
|
+
description: "Overrides the native MySQL column type.\n\n```atscript\n@db.mysql.type \"MEDIUMTEXT\"\nbio: string\n```",
|
|
64
|
+
nodeType: ["prop"],
|
|
65
|
+
multiple: false,
|
|
66
|
+
argument: {
|
|
67
|
+
name: "type",
|
|
68
|
+
type: "string",
|
|
69
|
+
description: "Native MySQL column type (e.g., \"MEDIUMTEXT\", \"TINYTEXT\")."
|
|
70
|
+
}
|
|
71
|
+
}),
|
|
72
|
+
onUpdate: new AnnotationSpec({
|
|
73
|
+
description: "Sets the MySQL ON UPDATE clause for a column.\n\n```atscript\n@db.mysql.onUpdate \"CURRENT_TIMESTAMP\"\nupdatedAt: number.timestamp\n```",
|
|
74
|
+
nodeType: ["prop"],
|
|
75
|
+
multiple: false,
|
|
76
|
+
argument: {
|
|
77
|
+
name: "expression",
|
|
78
|
+
type: "string",
|
|
79
|
+
values: ["CURRENT_TIMESTAMP"],
|
|
80
|
+
description: "Expression to evaluate on row update."
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
};
|
|
84
|
+
//#endregion
|
|
85
|
+
//#region src/plugin/index.ts
|
|
86
|
+
const MysqlPlugin = () => ({
|
|
87
|
+
name: "mysql",
|
|
88
|
+
config() {
|
|
89
|
+
return { annotations: { db: { mysql: annotations } } };
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
//#endregion
|
|
2
93
|
export { MysqlPlugin };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atscript/db-mysql",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.106",
|
|
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.
|
|
45
|
-
"@atscript/typescript": "^0.1.
|
|
44
|
+
"@atscript/core": "^0.1.76",
|
|
45
|
+
"@atscript/typescript": "^0.1.76",
|
|
46
46
|
"@uniqu/core": "^0.1.6",
|
|
47
47
|
"mysql2": "^3.11.0",
|
|
48
|
-
"unplugin-atscript": "^0.1.
|
|
48
|
+
"unplugin-atscript": "^0.1.76"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
|
-
"@atscript/core": "^0.1.
|
|
52
|
-
"@atscript/typescript": "^0.1.
|
|
51
|
+
"@atscript/core": "^0.1.76",
|
|
52
|
+
"@atscript/typescript": "^0.1.76",
|
|
53
53
|
"@uniqu/core": "^0.1.6",
|
|
54
54
|
"mysql2": ">=3.0.0",
|
|
55
|
-
"@atscript/db": "^0.1.
|
|
56
|
-
"@atscript/db-sql-tools": "^0.1.
|
|
55
|
+
"@atscript/db": "^0.1.106",
|
|
56
|
+
"@atscript/db-sql-tools": "^0.1.106"
|
|
57
57
|
},
|
|
58
58
|
"peerDependenciesMeta": {
|
|
59
59
|
"mysql2": {
|
package/dist/plugin-AzwuXV59.cjs
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
let _atscript_core = require("@atscript/core");
|
|
2
|
-
//#region src/plugin/annotations.ts
|
|
3
|
-
/**
|
|
4
|
-
* MySQL-specific annotations.
|
|
5
|
-
*
|
|
6
|
-
* Merged into the global config under `{ db: { mysql: ... } }` so they
|
|
7
|
-
* live alongside core's `@db.table`, `@db.index.*`, etc.
|
|
8
|
-
*
|
|
9
|
-
* These annotations opt-in to MySQL-specific behavior. Files using only
|
|
10
|
-
* portable `@db.*` annotations remain adapter-agnostic.
|
|
11
|
-
*/
|
|
12
|
-
const annotations = {
|
|
13
|
-
engine: new _atscript_core.AnnotationSpec({
|
|
14
|
-
description: "Specifies the MySQL storage engine.\n\n**Default:** `\"InnoDB\"`\n\n```atscript\n@db.mysql.engine \"MyISAM\"\nexport interface Logs { ... }\n```",
|
|
15
|
-
nodeType: ["interface"],
|
|
16
|
-
multiple: false,
|
|
17
|
-
argument: {
|
|
18
|
-
name: "engine",
|
|
19
|
-
type: "string",
|
|
20
|
-
values: [
|
|
21
|
-
"InnoDB",
|
|
22
|
-
"MyISAM",
|
|
23
|
-
"MEMORY",
|
|
24
|
-
"CSV",
|
|
25
|
-
"ARCHIVE"
|
|
26
|
-
],
|
|
27
|
-
description: "MySQL storage engine name."
|
|
28
|
-
}
|
|
29
|
-
}),
|
|
30
|
-
charset: new _atscript_core.AnnotationSpec({
|
|
31
|
-
description: "Specifies the character set for the table or column.\n\n**Default:** `\"utf8mb4\"`\n\n```atscript\n@db.mysql.charset \"latin1\"\nexport interface Legacy { ... }\n```",
|
|
32
|
-
nodeType: ["interface", "prop"],
|
|
33
|
-
multiple: false,
|
|
34
|
-
argument: {
|
|
35
|
-
name: "charset",
|
|
36
|
-
type: "string",
|
|
37
|
-
values: [
|
|
38
|
-
"utf8mb4",
|
|
39
|
-
"utf8",
|
|
40
|
-
"latin1",
|
|
41
|
-
"ascii",
|
|
42
|
-
"binary"
|
|
43
|
-
],
|
|
44
|
-
description: "MySQL character set name."
|
|
45
|
-
}
|
|
46
|
-
}),
|
|
47
|
-
collate: new _atscript_core.AnnotationSpec({
|
|
48
|
-
description: "Specifies a native MySQL collation (overrides portable `@db.column.collate`).\n\n```atscript\n@db.mysql.collate \"utf8mb4_turkish_ci\"\nname: string\n```",
|
|
49
|
-
nodeType: ["interface", "prop"],
|
|
50
|
-
multiple: false,
|
|
51
|
-
argument: {
|
|
52
|
-
name: "collation",
|
|
53
|
-
type: "string",
|
|
54
|
-
description: "Native MySQL collation name (e.g., \"utf8mb4_turkish_ci\")."
|
|
55
|
-
}
|
|
56
|
-
}),
|
|
57
|
-
unsigned: new _atscript_core.AnnotationSpec({
|
|
58
|
-
description: "Adds the UNSIGNED modifier to an integer column.\n\n```atscript\n@db.mysql.unsigned\nage: number.int\n```",
|
|
59
|
-
nodeType: ["prop"],
|
|
60
|
-
multiple: false
|
|
61
|
-
}),
|
|
62
|
-
type: new _atscript_core.AnnotationSpec({
|
|
63
|
-
description: "Overrides the native MySQL column type.\n\n```atscript\n@db.mysql.type \"MEDIUMTEXT\"\nbio: string\n```",
|
|
64
|
-
nodeType: ["prop"],
|
|
65
|
-
multiple: false,
|
|
66
|
-
argument: {
|
|
67
|
-
name: "type",
|
|
68
|
-
type: "string",
|
|
69
|
-
description: "Native MySQL column type (e.g., \"MEDIUMTEXT\", \"TINYTEXT\")."
|
|
70
|
-
}
|
|
71
|
-
}),
|
|
72
|
-
onUpdate: new _atscript_core.AnnotationSpec({
|
|
73
|
-
description: "Sets the MySQL ON UPDATE clause for a column.\n\n```atscript\n@db.mysql.onUpdate \"CURRENT_TIMESTAMP\"\nupdatedAt: number.timestamp\n```",
|
|
74
|
-
nodeType: ["prop"],
|
|
75
|
-
multiple: false,
|
|
76
|
-
argument: {
|
|
77
|
-
name: "expression",
|
|
78
|
-
type: "string",
|
|
79
|
-
values: ["CURRENT_TIMESTAMP"],
|
|
80
|
-
description: "Expression to evaluate on row update."
|
|
81
|
-
}
|
|
82
|
-
})
|
|
83
|
-
};
|
|
84
|
-
//#endregion
|
|
85
|
-
//#region src/plugin/index.ts
|
|
86
|
-
const MysqlPlugin = () => ({
|
|
87
|
-
name: "mysql",
|
|
88
|
-
config() {
|
|
89
|
-
return { annotations: { db: { mysql: annotations } } };
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
//#endregion
|
|
93
|
-
Object.defineProperty(exports, "MysqlPlugin", {
|
|
94
|
-
enumerable: true,
|
|
95
|
-
get: function() {
|
|
96
|
-
return MysqlPlugin;
|
|
97
|
-
}
|
|
98
|
-
});
|
package/dist/plugin-CPJY5K_I.mjs
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import { AnnotationSpec } from "@atscript/core";
|
|
2
|
-
//#region src/plugin/annotations.ts
|
|
3
|
-
/**
|
|
4
|
-
* MySQL-specific annotations.
|
|
5
|
-
*
|
|
6
|
-
* Merged into the global config under `{ db: { mysql: ... } }` so they
|
|
7
|
-
* live alongside core's `@db.table`, `@db.index.*`, etc.
|
|
8
|
-
*
|
|
9
|
-
* These annotations opt-in to MySQL-specific behavior. Files using only
|
|
10
|
-
* portable `@db.*` annotations remain adapter-agnostic.
|
|
11
|
-
*/
|
|
12
|
-
const annotations = {
|
|
13
|
-
engine: new AnnotationSpec({
|
|
14
|
-
description: "Specifies the MySQL storage engine.\n\n**Default:** `\"InnoDB\"`\n\n```atscript\n@db.mysql.engine \"MyISAM\"\nexport interface Logs { ... }\n```",
|
|
15
|
-
nodeType: ["interface"],
|
|
16
|
-
multiple: false,
|
|
17
|
-
argument: {
|
|
18
|
-
name: "engine",
|
|
19
|
-
type: "string",
|
|
20
|
-
values: [
|
|
21
|
-
"InnoDB",
|
|
22
|
-
"MyISAM",
|
|
23
|
-
"MEMORY",
|
|
24
|
-
"CSV",
|
|
25
|
-
"ARCHIVE"
|
|
26
|
-
],
|
|
27
|
-
description: "MySQL storage engine name."
|
|
28
|
-
}
|
|
29
|
-
}),
|
|
30
|
-
charset: new AnnotationSpec({
|
|
31
|
-
description: "Specifies the character set for the table or column.\n\n**Default:** `\"utf8mb4\"`\n\n```atscript\n@db.mysql.charset \"latin1\"\nexport interface Legacy { ... }\n```",
|
|
32
|
-
nodeType: ["interface", "prop"],
|
|
33
|
-
multiple: false,
|
|
34
|
-
argument: {
|
|
35
|
-
name: "charset",
|
|
36
|
-
type: "string",
|
|
37
|
-
values: [
|
|
38
|
-
"utf8mb4",
|
|
39
|
-
"utf8",
|
|
40
|
-
"latin1",
|
|
41
|
-
"ascii",
|
|
42
|
-
"binary"
|
|
43
|
-
],
|
|
44
|
-
description: "MySQL character set name."
|
|
45
|
-
}
|
|
46
|
-
}),
|
|
47
|
-
collate: new AnnotationSpec({
|
|
48
|
-
description: "Specifies a native MySQL collation (overrides portable `@db.column.collate`).\n\n```atscript\n@db.mysql.collate \"utf8mb4_turkish_ci\"\nname: string\n```",
|
|
49
|
-
nodeType: ["interface", "prop"],
|
|
50
|
-
multiple: false,
|
|
51
|
-
argument: {
|
|
52
|
-
name: "collation",
|
|
53
|
-
type: "string",
|
|
54
|
-
description: "Native MySQL collation name (e.g., \"utf8mb4_turkish_ci\")."
|
|
55
|
-
}
|
|
56
|
-
}),
|
|
57
|
-
unsigned: new AnnotationSpec({
|
|
58
|
-
description: "Adds the UNSIGNED modifier to an integer column.\n\n```atscript\n@db.mysql.unsigned\nage: number.int\n```",
|
|
59
|
-
nodeType: ["prop"],
|
|
60
|
-
multiple: false
|
|
61
|
-
}),
|
|
62
|
-
type: new AnnotationSpec({
|
|
63
|
-
description: "Overrides the native MySQL column type.\n\n```atscript\n@db.mysql.type \"MEDIUMTEXT\"\nbio: string\n```",
|
|
64
|
-
nodeType: ["prop"],
|
|
65
|
-
multiple: false,
|
|
66
|
-
argument: {
|
|
67
|
-
name: "type",
|
|
68
|
-
type: "string",
|
|
69
|
-
description: "Native MySQL column type (e.g., \"MEDIUMTEXT\", \"TINYTEXT\")."
|
|
70
|
-
}
|
|
71
|
-
}),
|
|
72
|
-
onUpdate: new AnnotationSpec({
|
|
73
|
-
description: "Sets the MySQL ON UPDATE clause for a column.\n\n```atscript\n@db.mysql.onUpdate \"CURRENT_TIMESTAMP\"\nupdatedAt: number.timestamp\n```",
|
|
74
|
-
nodeType: ["prop"],
|
|
75
|
-
multiple: false,
|
|
76
|
-
argument: {
|
|
77
|
-
name: "expression",
|
|
78
|
-
type: "string",
|
|
79
|
-
values: ["CURRENT_TIMESTAMP"],
|
|
80
|
-
description: "Expression to evaluate on row update."
|
|
81
|
-
}
|
|
82
|
-
})
|
|
83
|
-
};
|
|
84
|
-
//#endregion
|
|
85
|
-
//#region src/plugin/index.ts
|
|
86
|
-
const MysqlPlugin = () => ({
|
|
87
|
-
name: "mysql",
|
|
88
|
-
config() {
|
|
89
|
-
return { annotations: { db: { mysql: annotations } } };
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
//#endregion
|
|
93
|
-
export { MysqlPlugin as t };
|