Consolidated ASP Classic MVC framework from best components
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

615 wiersze
19KB

  1. '=======================================================================================================================
  2. ' MIGRATION RUNNER (Standalone VBScript)
  3. '=======================================================================================================================
  4. ' Runs database migrations directly via VBScript without requiring IIS/ASP.
  5. '
  6. ' Usage:
  7. ' cscript //nologo scripts\runMigrations.vbs [command]
  8. '
  9. ' Commands:
  10. ' up - Apply all pending migrations (default)
  11. ' down - Rollback the last migration
  12. ' status - Show migration status
  13. ' apply <file> - Apply a specific migration file
  14. ' rollback <file> - Rollback a specific migration file
  15. '
  16. ' Examples:
  17. ' cscript //nologo scripts\runMigrations.vbs
  18. ' cscript //nologo scripts\runMigrations.vbs up
  19. ' cscript //nologo scripts\runMigrations.vbs down
  20. ' cscript //nologo scripts\runMigrations.vbs status
  21. '
  22. Option Explicit
  23. Dim fso, scriptDir, projectRoot, webConfigPath, migrationsPath
  24. Dim connectionString, command, argument
  25. Set fso = CreateObject("Scripting.FileSystemObject")
  26. ' Get paths
  27. scriptDir = fso.GetParentFolderName(WScript.ScriptFullName)
  28. projectRoot = fso.GetAbsolutePathName(scriptDir & "\..")
  29. webConfigPath = projectRoot & "\public\web.config"
  30. migrationsPath = projectRoot & "\db\migrations"
  31. ' Parse arguments
  32. command = "up" ' default command
  33. If WScript.Arguments.Count > 0 Then
  34. command = LCase(WScript.Arguments(0))
  35. End If
  36. If WScript.Arguments.Count > 1 Then
  37. argument = WScript.Arguments(1)
  38. End If
  39. ' Validate command
  40. Select Case command
  41. Case "up", "down", "status", "apply", "rollback"
  42. ' Valid command
  43. Case Else
  44. ShowUsage
  45. WScript.Quit 1
  46. End Select
  47. ' Load connection string from web.config
  48. connectionString = GetConnectionString(webConfigPath)
  49. If connectionString = "" Then
  50. WScript.Echo "Error: Could not read connection string from web.config"
  51. WScript.Quit 1
  52. End If
  53. ' Execute the command
  54. On Error Resume Next
  55. Select Case command
  56. Case "up"
  57. ApplyAllPending
  58. Case "down"
  59. RollbackLast
  60. Case "status"
  61. ShowStatus
  62. Case "apply"
  63. If argument = "" Then
  64. WScript.Echo "Error: No migration file specified"
  65. WScript.Echo "Usage: cscript //nologo scripts\runMigrations.vbs apply <filename>"
  66. WScript.Quit 1
  67. End If
  68. ApplyMigration argument
  69. Case "rollback"
  70. If argument = "" Then
  71. WScript.Echo "Error: No migration file specified"
  72. WScript.Echo "Usage: cscript //nologo scripts\runMigrations.vbs rollback <filename>"
  73. WScript.Quit 1
  74. End If
  75. RollbackMigration argument
  76. End Select
  77. If Err.Number <> 0 Then
  78. WScript.Echo ""
  79. WScript.Echo "ERROR: " & Err.Description
  80. WScript.Echo "Number: " & Err.Number
  81. WScript.Echo "Source: " & Err.Source
  82. WScript.Quit 1
  83. End If
  84. WScript.Quit 0
  85. '=======================================================================================================================
  86. ' MIGRATION FUNCTIONS
  87. '=======================================================================================================================
  88. Sub ApplyAllPending()
  89. WScript.Echo "=============================================================="
  90. WScript.Echo "APPLYING PENDING MIGRATIONS"
  91. WScript.Echo "=============================================================="
  92. WScript.Echo ""
  93. EnsureSchemaMigrationsTable
  94. Dim pending, version, versions(), i, j, temp
  95. Set pending = GetPendingMigrations()
  96. If pending.Count = 0 Then
  97. WScript.Echo "No pending migrations."
  98. WScript.Echo ""
  99. WScript.Echo "=============================================================="
  100. WScript.Echo "DONE"
  101. WScript.Echo "=============================================================="
  102. Exit Sub
  103. End If
  104. ' Sort versions
  105. ReDim versions(pending.Count - 1)
  106. i = 0
  107. For Each version In pending.Keys
  108. versions(i) = version
  109. i = i + 1
  110. Next
  111. ' Simple bubble sort (string comparison works for YYYYMMDDHHMMSS format)
  112. For i = 0 To UBound(versions) - 1
  113. For j = i + 1 To UBound(versions)
  114. If versions(i) > versions(j) Then
  115. temp = versions(i)
  116. versions(i) = versions(j)
  117. versions(j) = temp
  118. End If
  119. Next
  120. Next
  121. ' Apply in order
  122. For i = 0 To UBound(versions)
  123. ApplyMigration pending(versions(i))
  124. Next
  125. WScript.Echo ""
  126. WScript.Echo "=============================================================="
  127. WScript.Echo "DONE"
  128. WScript.Echo "=============================================================="
  129. End Sub
  130. Sub RollbackLast()
  131. WScript.Echo "=============================================================="
  132. WScript.Echo "ROLLING BACK LAST MIGRATION"
  133. WScript.Echo "=============================================================="
  134. WScript.Echo ""
  135. EnsureSchemaMigrationsTable
  136. Dim applied, available, version, lastVersion, filename
  137. Set applied = GetAppliedMigrations()
  138. If applied.Count = 0 Then
  139. WScript.Echo "No migrations to rollback."
  140. WScript.Echo ""
  141. WScript.Echo "=============================================================="
  142. WScript.Echo "DONE"
  143. WScript.Echo "=============================================================="
  144. Exit Sub
  145. End If
  146. ' Find the last version (string comparison works for YYYYMMDDHHMMSS format)
  147. lastVersion = ""
  148. For Each version In applied.Keys
  149. If lastVersion = "" Or version > lastVersion Then
  150. lastVersion = version
  151. End If
  152. Next
  153. ' Find the filename
  154. Set available = GetAvailableMigrations()
  155. If available.Exists(lastVersion) Then
  156. filename = available(lastVersion)
  157. RollbackMigration filename
  158. Else
  159. WScript.Echo "Error: Migration file not found for version: " & lastVersion
  160. WScript.Quit 1
  161. End If
  162. WScript.Echo ""
  163. WScript.Echo "=============================================================="
  164. WScript.Echo "DONE"
  165. WScript.Echo "=============================================================="
  166. End Sub
  167. Sub ShowStatus()
  168. WScript.Echo "=============================================================="
  169. WScript.Echo "MIGRATION STATUS"
  170. WScript.Echo "=============================================================="
  171. WScript.Echo ""
  172. EnsureSchemaMigrationsTable
  173. Dim applied, pending, available, version
  174. Set applied = GetAppliedMigrations()
  175. Set pending = GetPendingMigrations()
  176. Set available = GetAvailableMigrations()
  177. WScript.Echo "Applied migrations: " & applied.Count
  178. If applied.Count > 0 Then
  179. For Each version In applied.Keys
  180. If available.Exists(version) Then
  181. WScript.Echo " [X] " & available(version)
  182. Else
  183. WScript.Echo " [X] " & version & " (file not found)"
  184. End If
  185. Next
  186. End If
  187. WScript.Echo ""
  188. WScript.Echo "Pending migrations: " & pending.Count
  189. If pending.Count > 0 Then
  190. For Each version In pending.Keys
  191. WScript.Echo " [ ] " & pending(version)
  192. Next
  193. End If
  194. WScript.Echo ""
  195. WScript.Echo "=============================================================="
  196. End Sub
  197. Sub ApplyMigration(filename)
  198. Dim version
  199. version = GetVersionFromFilename(filename)
  200. If version = "" Then
  201. Err.Raise vbObjectError + 1, "Migrator", "Invalid migration filename format: " & filename
  202. End If
  203. ' Check if already applied
  204. Dim applied
  205. Set applied = GetAppliedMigrations()
  206. If applied.Exists(version) Then
  207. WScript.Echo "Migration " & version & " already applied. Skipping."
  208. Exit Sub
  209. End If
  210. WScript.Echo "Applying migration: " & filename & "..."
  211. ' Execute the migration
  212. ' NOTE: Access/Jet does NOT support DDL (CREATE TABLE, etc.) inside transactions
  213. ' So we run without transaction wrapper and rely on error checking instead
  214. Dim conn, migrationSuccess
  215. Set conn = GetConnection()
  216. On Error Resume Next
  217. migrationSuccess = ExecuteMigrationFile(filename, "Up", conn)
  218. If Err.Number <> 0 Then
  219. WScript.Echo "ERROR: Migration failed - " & Err.Description
  220. WScript.Echo "Error Number: " & Err.Number
  221. conn.Close
  222. Err.Raise vbObjectError + 2, "Migrator", "Migration failed: " & Err.Description
  223. End If
  224. On Error GoTo 0
  225. ' Check if migration had SQL errors
  226. If Not migrationSuccess Then
  227. WScript.Echo ""
  228. WScript.Echo "ERROR: Migration failed due to SQL errors (see above)."
  229. WScript.Echo "Migration NOT recorded. Please fix the migration and try again."
  230. conn.Close
  231. WScript.Quit 1
  232. End If
  233. ' Record the migration
  234. On Error Resume Next
  235. Dim cmd
  236. Set cmd = CreateObject("ADODB.Command")
  237. Set cmd.ActiveConnection = conn
  238. cmd.CommandText = "INSERT INTO schema_migrations (version, applied_at) VALUES (?, ?)"
  239. cmd.Execute , Array(version, Now())
  240. If Err.Number <> 0 Then
  241. WScript.Echo "ERROR: Failed to record migration - " & Err.Description
  242. conn.Close
  243. Err.Raise vbObjectError + 3, "Migrator", "Failed to record migration: " & Err.Description
  244. End If
  245. On Error GoTo 0
  246. conn.Close
  247. WScript.Echo "Migration " & version & " applied successfully."
  248. End Sub
  249. Sub RollbackMigration(filename)
  250. Dim version
  251. version = GetVersionFromFilename(filename)
  252. If version = "" Then
  253. Err.Raise vbObjectError + 1, "Migrator", "Invalid migration filename format: " & filename
  254. End If
  255. ' Check if applied
  256. Dim applied
  257. Set applied = GetAppliedMigrations()
  258. If Not applied.Exists(version) Then
  259. WScript.Echo "Migration " & version & " not applied. Skipping."
  260. Exit Sub
  261. End If
  262. WScript.Echo "Rolling back migration: " & filename & "..."
  263. ' Execute the migration
  264. ' NOTE: Access/Jet does NOT support DDL (DROP TABLE, etc.) inside transactions
  265. Dim conn, rollbackSuccess
  266. Set conn = GetConnection()
  267. On Error Resume Next
  268. rollbackSuccess = ExecuteMigrationFile(filename, "Down", conn)
  269. If Err.Number <> 0 Then
  270. WScript.Echo "ERROR: Rollback failed - " & Err.Description
  271. WScript.Echo "Error Number: " & Err.Number
  272. conn.Close
  273. Err.Raise vbObjectError + 4, "Migrator", "Rollback failed: " & Err.Description
  274. End If
  275. On Error GoTo 0
  276. ' Check if rollback had SQL errors
  277. If Not rollbackSuccess Then
  278. WScript.Echo ""
  279. WScript.Echo "ERROR: Rollback failed due to SQL errors (see above)."
  280. WScript.Echo "Migration record NOT removed. Please fix the issue manually."
  281. conn.Close
  282. WScript.Quit 1
  283. End If
  284. ' Remove migration record
  285. On Error Resume Next
  286. Dim cmd
  287. Set cmd = CreateObject("ADODB.Command")
  288. Set cmd.ActiveConnection = conn
  289. cmd.CommandText = "DELETE FROM schema_migrations WHERE version = ?"
  290. cmd.Execute , Array(version)
  291. If Err.Number <> 0 Then
  292. WScript.Echo "ERROR: Failed to remove migration record - " & Err.Description
  293. conn.Close
  294. Err.Raise vbObjectError + 5, "Migrator", "Failed to remove migration record: " & Err.Description
  295. End If
  296. On Error GoTo 0
  297. conn.Close
  298. WScript.Echo "Migration " & version & " rolled back successfully."
  299. End Sub
  300. '=======================================================================================================================
  301. ' HELPER FUNCTIONS
  302. '=======================================================================================================================
  303. Sub ShowUsage()
  304. WScript.Echo "Usage: cscript //nologo scripts\runMigrations.vbs [command]"
  305. WScript.Echo ""
  306. WScript.Echo "Commands:"
  307. WScript.Echo " up - Apply all pending migrations (default)"
  308. WScript.Echo " down - Rollback the last migration"
  309. WScript.Echo " status - Show migration status"
  310. WScript.Echo " apply <file> - Apply a specific migration file"
  311. WScript.Echo " rollback <file> - Rollback a specific migration file"
  312. WScript.Echo ""
  313. WScript.Echo "Examples:"
  314. WScript.Echo " cscript //nologo scripts\runMigrations.vbs"
  315. WScript.Echo " cscript //nologo scripts\runMigrations.vbs up"
  316. WScript.Echo " cscript //nologo scripts\runMigrations.vbs down"
  317. WScript.Echo " cscript //nologo scripts\runMigrations.vbs status"
  318. End Sub
  319. Function GetConnectionString(configPath)
  320. If Not fso.FileExists(configPath) Then
  321. GetConnectionString = ""
  322. Exit Function
  323. End If
  324. Dim xmlDoc, node
  325. Set xmlDoc = CreateObject("Microsoft.XMLDOM")
  326. xmlDoc.async = False
  327. xmlDoc.load configPath
  328. Set node = xmlDoc.selectSingleNode("//appSettings/add[@key='ConnectionString']/@value")
  329. If node Is Nothing Then
  330. GetConnectionString = ""
  331. Else
  332. GetConnectionString = node.text
  333. End If
  334. End Function
  335. Function GetConnection()
  336. Dim conn
  337. Set conn = CreateObject("ADODB.Connection")
  338. conn.Open connectionString
  339. Set GetConnection = conn
  340. End Function
  341. Sub EnsureSchemaMigrationsTable()
  342. Dim conn, rs
  343. Set conn = GetConnection()
  344. On Error Resume Next
  345. Set rs = conn.Execute("SELECT TOP 1 version FROM schema_migrations")
  346. If Err.Number <> 0 Then
  347. ' Table doesn't exist, create it
  348. Err.Clear
  349. On Error GoTo 0
  350. conn.Execute "CREATE TABLE schema_migrations (" & _
  351. "version VARCHAR(14) PRIMARY KEY, " & _
  352. "applied_at DATETIME NOT NULL)"
  353. Else
  354. If Not rs Is Nothing Then
  355. If Not rs.EOF Then rs.Close
  356. End If
  357. End If
  358. conn.Close
  359. On Error GoTo 0
  360. End Sub
  361. Function GetAppliedMigrations()
  362. Dim conn, rs, versions, version
  363. Set conn = GetConnection()
  364. Set versions = CreateObject("Scripting.Dictionary")
  365. Set rs = conn.Execute("SELECT version FROM schema_migrations ORDER BY version")
  366. Do While Not rs.EOF
  367. version = Trim(rs("version"))
  368. versions.Add version, True
  369. rs.MoveNext
  370. Loop
  371. rs.Close
  372. conn.Close
  373. Set GetAppliedMigrations = versions
  374. End Function
  375. Function GetAvailableMigrations()
  376. Dim folder, files, file, migrations, version
  377. Set migrations = CreateObject("Scripting.Dictionary")
  378. If Not fso.FolderExists(migrationsPath) Then
  379. Set GetAvailableMigrations = migrations
  380. Exit Function
  381. End If
  382. Set folder = fso.GetFolder(migrationsPath)
  383. Set files = folder.Files
  384. For Each file In files
  385. If LCase(fso.GetExtensionName(file.Name)) = "asp" Then
  386. version = GetVersionFromFilename(file.Name)
  387. If version <> "" Then
  388. migrations.Add version, file.Name
  389. End If
  390. End If
  391. Next
  392. Set GetAvailableMigrations = migrations
  393. End Function
  394. Function GetPendingMigrations()
  395. Dim applied, available, pending, version
  396. Set applied = GetAppliedMigrations()
  397. Set available = GetAvailableMigrations()
  398. Set pending = CreateObject("Scripting.Dictionary")
  399. For Each version In available.Keys
  400. If Not applied.Exists(version) Then
  401. pending.Add version, available(version)
  402. End If
  403. Next
  404. Set GetPendingMigrations = pending
  405. End Function
  406. Function GetVersionFromFilename(filename)
  407. Dim parts
  408. parts = Split(filename, "_")
  409. If UBound(parts) >= 0 Then
  410. Dim version
  411. version = parts(0)
  412. ' Validate it's a 14-digit timestamp
  413. If Len(version) = 14 And IsNumeric(version) Then
  414. GetVersionFromFilename = version
  415. Exit Function
  416. End If
  417. End If
  418. GetVersionFromFilename = ""
  419. End Function
  420. ' Global variable to hold migration context for error checking
  421. Dim g_migrationContext
  422. Function ExecuteMigrationFile(filename, direction, conn)
  423. Dim migrationPath, fileContent, migration
  424. migrationPath = migrationsPath & "\" & filename
  425. If Not fso.FileExists(migrationPath) Then
  426. Err.Raise vbObjectError + 6, "Migrator", "Migration file not found: " & migrationPath
  427. End If
  428. ' Create migration context
  429. Set migration = New MigrationContext
  430. Set migration.Connection = conn
  431. Set g_migrationContext = migration ' Store globally for error checking
  432. ' Read and execute the migration file
  433. Dim stream
  434. Set stream = fso.OpenTextFile(migrationPath, 1)
  435. fileContent = stream.ReadAll
  436. stream.Close
  437. ' Remove ASP tags
  438. fileContent = Replace(fileContent, "<%", "")
  439. fileContent = Replace(fileContent, "%>", "")
  440. ' Execute the migration code
  441. ExecuteGlobal fileContent
  442. ' Call the appropriate method
  443. If direction = "Up" Then
  444. Migration_Up migration
  445. ElseIf direction = "Down" Then
  446. Migration_Down migration
  447. End If
  448. ' Return True if successful, False if error
  449. ExecuteMigrationFile = Not migration.HasError
  450. End Function
  451. '=======================================================================================================================
  452. ' MIGRATION CONTEXT CLASS
  453. '=======================================================================================================================
  454. Class MigrationContext
  455. Public Connection
  456. Public HasError
  457. Public LastErrorMessage
  458. Public LastErrorNumber
  459. Private Sub Class_Initialize()
  460. HasError = False
  461. LastErrorMessage = ""
  462. LastErrorNumber = 0
  463. End Sub
  464. Public Sub ExecuteSQL(sql)
  465. If HasError Then Exit Sub ' Skip if previous error occurred
  466. On Error Resume Next
  467. Connection.Execute sql
  468. If Err.Number <> 0 Then
  469. HasError = True
  470. LastErrorNumber = Err.Number
  471. LastErrorMessage = "SQL Error: " & Err.Description & " (Error " & Err.Number & ")" & vbCrLf & "SQL: " & sql
  472. WScript.Echo " ERROR executing SQL: " & Err.Description
  473. WScript.Echo " Error Number: " & Err.Number
  474. WScript.Echo " SQL: " & sql
  475. Err.Clear
  476. End If
  477. On Error GoTo 0
  478. End Sub
  479. Public Sub CreateTable(tableName, columns)
  480. If HasError Then Exit Sub
  481. WScript.Echo " Creating table: " & tableName
  482. ExecuteSQL "CREATE TABLE " & tableName & " (" & columns & ")"
  483. End Sub
  484. Public Sub DropTable(tableName)
  485. If HasError Then Exit Sub
  486. WScript.Echo " Dropping table: " & tableName
  487. ExecuteSQL "DROP TABLE " & tableName
  488. End Sub
  489. Public Sub AddColumn(tableName, columnName, columnType)
  490. If HasError Then Exit Sub
  491. WScript.Echo " Adding column: " & tableName & "." & columnName
  492. ExecuteSQL "ALTER TABLE " & tableName & " ADD COLUMN " & columnName & " " & columnType
  493. End Sub
  494. Public Sub DropColumn(tableName, columnName)
  495. If HasError Then Exit Sub
  496. WScript.Echo " Dropping column: " & tableName & "." & columnName
  497. ExecuteSQL "ALTER TABLE " & tableName & " DROP COLUMN " & columnName
  498. End Sub
  499. Public Sub CreateIndex(indexName, tableName, columns)
  500. If HasError Then Exit Sub
  501. WScript.Echo " Creating index: " & indexName & " on " & tableName
  502. ExecuteSQL "CREATE INDEX " & indexName & " ON " & tableName & " (" & columns & ")"
  503. End Sub
  504. Public Sub DropIndex(indexName, tableName)
  505. If HasError Then Exit Sub
  506. WScript.Echo " Dropping index: " & indexName & " on " & tableName
  507. ExecuteSQL "DROP INDEX " & indexName & " ON " & tableName
  508. End Sub
  509. Public Function DB()
  510. Set DB = Me
  511. End Function
  512. End Class

Powered by TurnKey Linux.