├── .gitignore ├── .editorconfig ├── helper.yml ├── README.md ├── sqlyoga_loader.livecodescript ├── LICENSE ├── sqlyoga_levure_callbacks.livecodescript ├── sqlyoga_development.livecodescript └── sqlyoga_schema.livecodescript /.gitignore: -------------------------------------------------------------------------------- 1 | # workspace files are user-specific 2 | *.sublime-workspace 3 | 4 | # Misc 5 | .env 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true 13 | indent_style = space 14 | indent_size = 2 15 | 16 | [*.md] 17 | trim_trailing_whitespace = false 18 | -------------------------------------------------------------------------------- /helper.yml: -------------------------------------------------------------------------------- 1 | libraries: 2 | - filename: sqlyoga.livecodescript 3 | - filename: sqlyoga_schema.livecodescript 4 | - filename: sqlyoga_loader.livecodescript 5 | frontscripts: 6 | - filename: sqlyoga_development.livecodescript 7 | distribute: false 8 | register components: 9 | - key: sql yoga>configuration 10 | kind: files 11 | callback stackfile: sqlyoga_levure_callbacks.livecodescript 12 | distribute: false 13 | packager callbacks stackfile: sqlyoga_levure_callbacks.livecodescript 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | sql-yoga 2 | ======== 3 | 4 | SQL Yoga database library for LiveCode 5 | 6 | SQL Yoga is a database library for LiveCode built on top of RevDB or Valentina. Using the database schema and information from you, the developer, SQL Yoga allows you to write less code when querying and updating an application's database. 7 | 8 | While the library can be used independently of any framework, SQL Yoga is distributed as a Helper for the [Levure framework](https://www.github.com/trevordevore/levure). Levure makes using SQL Yoga much easier from a configuration and customization perspective. 9 | 10 | The SQL Yoga documentation can be found in the [repo wiki](https://github.com/trevordevore/sql-yoga/wiki). That is where you will find instructions on getting started. 11 | 12 | SQL Yoga works with LiveCode 8 or newer. 13 | -------------------------------------------------------------------------------- /sqlyoga_loader.livecodescript: -------------------------------------------------------------------------------- 1 | script "SQL Yoga Database Object Loader" 2 | /** 3 | Summary: Used in deployment to load the database objects. 4 | 5 | Description: 6 | When put in use this library will load the databse objects stored in the uDbObjects 7 | custom property set. 8 | 9 | */ 10 | on libraryStack 11 | if the target is not me then pass libraryStack 12 | 13 | if "uDbObjects" is among the lines of the customPropertySets of me then 14 | repeat for each line tDatabaseKey in the customKeys["uDbObjects"] of me 15 | dbobject_initialize the uDbObjects[tDatabaseKey] of me, levureAppFolder(), tDatabaseKey 16 | end repeat 17 | end if 18 | 19 | send "__removeSQLYogaLoaderFromMemory" to stack "libSQLYoga" in 0 seconds 20 | stop using me 21 | end libraryStack 22 | 23 | 24 | command __removeSQLYogaLoaderFromMemory 25 | delete stack "SQL Yoga Database Object Loader" 26 | end __removeSQLYogaLoaderFromMemory 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-2018 Trevor DeVore 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /sqlyoga_levure_callbacks.livecodescript: -------------------------------------------------------------------------------- 1 | script "SQLYoga Levure Callbacks" 2 | command loadRegisteredKeyFile @xFileA 3 | local tDBObjectA, tDataA, tError 4 | 5 | _concatYAMLFiles xFileA["filename"] 6 | put the result into tError 7 | put it into tDataA 8 | 9 | if tError is empty then 10 | dbobject_initialize tDataA, levureAppFolder(), xFileA["database key"] 11 | put the result into tError 12 | end if 13 | 14 | if tError is empty then 15 | return empty for value 16 | else 17 | return tError for error 18 | end if 19 | end loadRegisteredKeyFile 20 | 21 | 22 | private command _concatYAMLFiles pFilename 23 | local tError, tFile, tData, tDataA 24 | 25 | if there is a folder pFilename then 26 | repeat for each line tFile in files(pFilename) 27 | if not (tFile ends with ".yml" or tFile ends with ".yaml") then next repeat 28 | 29 | put url("binfile:" & pFilename & "/" & tFile) & cr after tData 30 | if the result is not empty then 31 | put "unable to read schema yaml file" && pFilename & "/" & tFile && ":" && the result into tError 32 | exit repeat 33 | end if 34 | end repeat 35 | else 36 | put url("binfile:" & pFilename) into tData 37 | end if 38 | 39 | if tError is empty then 40 | YAMLToArray textDecode(tData, "utf8") 41 | put the result into tError 42 | put it into tDataA 43 | end if 44 | 45 | if tError is empty then 46 | return tDataA for value 47 | else 48 | return tError for error 49 | end if 50 | end _concatYAMLFiles 51 | 52 | 53 | /** 54 | Summary: Concat all of the files and store in a custom property in sqlyoga_loader.livecodescript 55 | 56 | Description: 57 | This helper doesn't distribute the files in the sql yoga > configuration key. In 58 | development the designated folder holds multiple files. Concat the files and make available 59 | through a custom property. 60 | */ 61 | command processPackagedFileAsset pBuildProfile, pPlatform, @xFileAssetA, pOutputFolder 62 | local tError, tDataA, tFilename 63 | local msgsAreLocked 64 | 65 | if xFileAssetA["database key"] is empty then put "default" into xFileAssetA["database key"] 66 | 67 | _concatYAMLFiles xFileAssetA["filename"] 68 | put the result into tError 69 | put it into tDataA 70 | 71 | if tError is empty then 72 | # Store the database object in the sqlyoga library stack 73 | put pOutputFolder & "/helpers/sql-yoga/sqlyoga_loader.livecodescript" into tFilename 74 | put the lockMessages into msgsAreLocked 75 | lock messages 76 | set the uDbObjects[ xFileAssetA["database key"] ] of stack tFilename to tDataA 77 | set the scriptonly of stack tFilename to false 78 | save stack tFilename 79 | delete stack tFilename 80 | unlock messages 81 | end if 82 | 83 | if tError is not empty then 84 | answer error "SQL Yoga Helper:" && param(0) & ":" && tError 85 | end if 86 | 87 | return empty 88 | end processPackagedFileAsset 89 | -------------------------------------------------------------------------------- /sqlyoga_development.livecodescript: -------------------------------------------------------------------------------- 1 | script "SQL Yoga Development Tools" 2 | /** 3 | Summary: Reloads all of the SQL Yoga configuration files. 4 | 5 | Description: 6 | This handler is useful for reloading configuration files after 7 | you modify them. 8 | */ 9 | command sqlyogadev_reloadConfigFiles 10 | levureReloadRegisteredKey "sql yoga>configuration" 11 | return empty 12 | end sqlyogadev_reloadConfigFiles 13 | 14 | 15 | /** 16 | Summary: Turns on logging using the Levure logging helper. 17 | 18 | Description: 19 | All log entries generated by SQL Yoga will be prefixed with "sqlYoga:" 20 | */ 21 | command sqlyogadev_enableLogging pBoolean, pConnectionName, pDBKey 22 | if pBoolean then 23 | dbconn_set "log callback", "sqlyogadev_loggingCallback", pConnectionName, pDBKey 24 | else 25 | dbconn_set "log callback", "", pConnectionName, pDBKey 26 | end if 27 | 28 | return empty 29 | end sqlyogadev_enableLogging 30 | 31 | 32 | command sqlyogadev_loggingCallback pSQL, pConnectionName, pDBKey 33 | loggerLogMsg "sqlYoga:" && pSQL 34 | end sqlyogadev_loggingCallback 35 | 36 | 37 | /** 38 | Summary: Saves the current schema for a database to a YAML file. 39 | 40 | Description: 41 | SQL Yoga will load the schema.yml file if present and use that to define 42 | the table objects for the database. If the schema.yml file is not present 43 | then the first time a connection is made to the database the tables and 44 | columns will be extracted from the database. 45 | 46 | A schema.yml can be used to create a new database. Just create a connection 47 | to an empty database and call `dbsynch_databaseWithSchema`. 48 | */ 49 | command sqlyogadev_saveSchemaToYAML pDBKey 50 | local tError 51 | 52 | _saveSchemaToYAML pDBKey 53 | put the result into tError 54 | 55 | return tError for error 56 | end sqlyogadev_saveSchemaToYAML 57 | 58 | 59 | command sqlyogadev_createSchemaMigration pName, pDBKey 60 | local tError, tMigrateFolder, tStamp 61 | 62 | put _migrateFolder(pDBKey) into tMigrateFolder 63 | put _GetGMTTimeStampForMigrationId() into tStamp 64 | 65 | put textEncode("migration:" & cr & " ", "utf8") \ 66 | into URL ("binfile:" & tMigrateFolder & "/" & tStamp & "_" & pName & ".yml") 67 | put the result into tError 68 | 69 | return tError for error 70 | end sqlyogadev_createSchemaMigration 71 | 72 | 73 | /** 74 | Summary: Runs all migrations in the *migrate* folder that have not been applied to the current connection. 75 | 76 | Description: 77 | This handler will loop through all files in the *migrate* folder. The schema version 78 | for the migration will be determined by the numbers at the front of the filename. 79 | Each number is compared against the current `schema` table `version` column value. 80 | If the version of the migration is greater than the version of the column value then 81 | the migration is applied to the database. 82 | 83 | After running the last migration the version of the migration is stored in the 84 | `schema` table `version` column. 85 | */ 86 | command sqlyogadev_runMigrations pConnectionName, pDBKey 87 | local tError, tConfigA, i, tMigrateFolder 88 | 89 | put _migrateFolder(pDBKey) into tMigrateFolder 90 | 91 | if there is a folder tMigrateFolder then 92 | _runMigrations tMigrateFolder, pConnectionName, pDBKey 93 | put the result into tError 94 | else 95 | put "migrate folder not found:" && tMigrateFolder into tError 96 | end if 97 | 98 | if tError is empty then 99 | _saveSchemaToYAML pDBKey 100 | put the result into tError 101 | end if 102 | 103 | return tError 104 | end sqlyogadev_runMigrations 105 | 106 | 107 | command _runMigrations pMigrationFolder, pConnectionName, pDBKey 108 | local tError, tFiles, tFile, tMigrateA 109 | local tMigrationVersion 110 | 111 | put files(pMigrationFolder) into tFiles 112 | filter tFiles without ".*" 113 | filter tFiles with regex pattern "(.*\.yml$)|(.*\.livecodescript$)" 114 | 115 | sort lines of tFiles numeric by each 116 | 117 | repeat for each line tFile in tFiles 118 | put _extractMigrationVersionFromFilename(tFile) into tMigrationVersion 119 | 120 | if tFile ends with ".yml" or tFile ends with ".yaml" then 121 | put yamlFileToArray(pMigrationFolder & "/" & tFile) into tMigrateA 122 | put the result into tError 123 | 124 | if tError is empty then 125 | dbschema_processMigrationArray tMigrationVersion, tMigrateA, pConnectionName, pDBKey 126 | end if 127 | else if tFile ends with ".livecodescript" or tFile ends with ".livecode" then 128 | dbschema_processMigrationFile tMigrationVersion, pMigrationFolder & "/" & tFile, pConnectionName, pDBKey 129 | end if 130 | 131 | if tError is not empty then exit repeat 132 | end repeat 133 | 134 | return tError 135 | end _runMigrations 136 | 137 | 138 | private function _extractMigrationVersionFromFilename pFilename 139 | local tChar, tVersion 140 | 141 | repeat for each char tChar in pFilename 142 | if tChar is a number then 143 | put tChar after tVersion 144 | end if 145 | end repeat 146 | 147 | return tVersion 148 | end _extractMigrationVersionFromFilename 149 | 150 | 151 | private command _saveSchemaToYAML pDBKey 152 | local tError, tDBFolder, tYAML 153 | 154 | put _databaseFolder() into tDBFolder 155 | 156 | if there is a folder tDBFolder then 157 | put dbschema_get("yaml", pDBKey) into tYAML 158 | put textEncode(tYAML, "utf8") into URL("binfile:" & tDBFolder & "/schema.yml") 159 | put the result into tError 160 | end if 161 | 162 | return tError 163 | end _saveSchemaToYAML 164 | 165 | 166 | private function _databaseFolder pDBKey 167 | local tConfigA, i 168 | 169 | put levureAppGet("sql yoga") into tConfigA 170 | 171 | repeat with i = 1 to the number of elements of tConfigA["configuration"] 172 | if tConfigA["configuration"][i]["database key"] is pDBKey \ 173 | or (pDBKey is empty and tConfigA["configuration"][i]["database key"] is "default") then 174 | return tConfigA["configuration"][i]["filename"] 175 | end if 176 | end repeat 177 | 178 | return empty 179 | end _databaseFolder 180 | 181 | 182 | private function _migrateFolder pDBKey 183 | local tDBFolder 184 | 185 | put _databaseFolder(pDBKey) into tDBFolder 186 | if tDBFolder is not empty then 187 | return tDBFolder & "/migrate" 188 | else 189 | return empty 190 | end if 191 | end _migrateFolder 192 | 193 | 194 | private function _GetGMTTimeStampForMigrationId 195 | local tInternetTime, tGMTOffset 196 | 197 | put the internet date into tInternetTime 198 | put _InternetTimeStampGMTOffset(tInternetTime) into tGMTOffset 199 | 200 | convert tInternetTime from internet date to dateItems 201 | if tGMTOffset is a number then 202 | subtract tGMTOffset from item 4 of tInternetTime 203 | end if 204 | convert tInternetTime to dateItems 205 | replace comma with empty in tInternetTime 206 | return tInternetTime 207 | end _GetGMTTimeStampForMigrationId 208 | 209 | 210 | private function _InternetTimeStampGMTOffset pInternetTime 211 | local tGMTOffset 212 | 213 | put the last word of pInternetTime into tGMTOffset 214 | delete the char -2 to -1 of tGMTOffset 215 | if char 2 of tGMTOffset is 0 then delete char 2 of tGMTOffset 216 | if char 1 of tGMTOffset is "+" then delete char 1 of tGMTOffset 217 | return tGMTOffset 218 | end _InternetTimeStampGMTOffset 219 | -------------------------------------------------------------------------------- /sqlyoga_schema.livecodescript: -------------------------------------------------------------------------------- 1 | script "libSQLYogaSchema" 2 | constant kErrErrorInFunction = 219 3 | constant kErrCantFindObject = 619 4 | 5 | 6 | command dbschema_load pSchemaFile 7 | # code... 8 | end dbschema_load 9 | 10 | 11 | /** 12 | Summary: Dispatches `RunMigration` to a stack file. 13 | 14 | Parameters: 15 | pMigrationVersion: 16 | pStackFilename: 17 | pConnectionA: 18 | pDBKey: 19 | 20 | Description: 21 | 22 | */ 23 | command dbschema_processMigrationFile pMigrationVersion, pStackFilename, pConnectionA, pDBKey 24 | local tError 25 | 26 | _beginProcessMigration pMigrationVersion, pConnectionA, pDBKey 27 | if the result is not empty then return empty 28 | 29 | try 30 | dispatch "RunMigration" to stack pStackFilename with pConnectionA 31 | dbschema_createTriggers pConnectionA 32 | _endProcessMigration pMigrationVersion, pConnectionA 33 | dbsynch_schemaWithDatabase pConnectionA, pDBKey 34 | catch tError 35 | 36 | finally 37 | if there is a stack pStackFilename then 38 | delete stack pStackFilename 39 | end if 40 | 41 | if tError is empty then 42 | dbconn_commitTransaction empty, pConnectionA 43 | else 44 | dbconn_rollbackTransaction empty, pConnectionA 45 | throw tError 46 | end if 47 | end try 48 | 49 | return empty 50 | end dbschema_processMigrationFile 51 | 52 | 53 | /** 54 | Summary: 55 | 56 | */ 57 | command dbschema_processMigrationArray pMigrationVersion, pMigrationA, pConnectionA, pDBKey 58 | local tError, tTableA, tIndexA, tFieldA, tFieldsA 59 | 60 | _beginProcessMigration pMigrationVersion, pConnectionA, pDBKey 61 | if the result is not empty then return empty 62 | 63 | try 64 | repeat for each element tTableA in pMigrationA["migration"]["create tables"] 65 | 66 | # Get fields into format that API expects 67 | if tTableA["field order"] is empty then 68 | repeat for each element tFieldA in tTableA["fields"] 69 | put tFieldA["name"] & "," after tTableA["field order"] 70 | put tFieldA into tFieldsA[tFieldA["name"]] 71 | end repeat 72 | delete the last char of tTableA["field order"] 73 | end if 74 | put tFieldsA into tTableA["fields"] 75 | 76 | dbschema_createTable tTableA["name"], tTableA, pConnectionA, pDBKey 77 | end repeat 78 | 79 | repeat for each element tTableA in pMigrationA["migration"]["create fields"] 80 | dbschema_createFields tTableA["table"], tTableA["fields"], pConnectionA, pDBKey 81 | end repeat 82 | 83 | repeat for each element tIndexA in pMigrationA["migration"]["create indexes"] 84 | dbschema_createIndex tIndexA["name"], tIndexA, pConnectionA, pDBKey 85 | end repeat 86 | 87 | dbschema_createTriggers pConnectionA 88 | _endProcessMigration pMigrationVersion, pConnectionA 89 | 90 | dbsynch_schemaWithDatabase pConnectionA, pDBKey 91 | 92 | local tRecA, tField 93 | 94 | repeat for each element tTableA in pMigrationA["migration"]["create records"] 95 | repeat for each element tRecordA in tTableA["records"] 96 | put sqlrecord_createObject(tTableA["table"], pConnectionA, pDBKey) into tRecA 97 | repeat for each key tField in tRecordA 98 | sqlrecord_set tRecA, tField, tRecordA[tField] 99 | end repeat 100 | sqlrecord_create tRecA 101 | end repeat 102 | end repeat 103 | catch tError 104 | finally 105 | if tError is empty then 106 | dbconn_commitTransaction empty, pConnectionA 107 | else 108 | dbconn_rollbackTransaction empty, pConnectionA 109 | throw tError 110 | end if 111 | end try 112 | 113 | return empty 114 | end dbschema_processMigrationArray 115 | 116 | 117 | private command _beginProcessMigration pMigrationVersion, @rConnectionA, pDBKey 118 | local tError 119 | 120 | put dbobject_createConnectionArray(rConnectionA, pDBKey) into rConnectionA 121 | 122 | dbconn_connect rConnectionA 123 | _createSchemaMigrationsTable rConnectionA 124 | _checkSchemaMigrationShouldRun pMigrationVersion, rConnectionA 125 | put the result into tError 126 | 127 | if tError is empty then 128 | dbconn_beginTransaction empty, rConnectionA 129 | end if 130 | 131 | return tError for error 132 | end _beginProcessMigration 133 | 134 | 135 | private command _endProcessMigration pMigrationVersion, pConnectionA 136 | _insertSchemaMigration pMigrationVersion, pConnectionA 137 | 138 | return empty 139 | end _endProcessMigration 140 | 141 | 142 | private command _checkSchemaMigrationShouldRun pMigrationVersion, pConnectionA 143 | local tError, tSQL, tVersion 144 | 145 | put _getCurrentSchemaVersion(pConnectionA) into tVersion 146 | if tVersion >= pMigrationVersion then 147 | put "migration has already been applied" into tError 148 | end if 149 | 150 | return tError for error 151 | end _checkSchemaMigrationShouldRun 152 | 153 | 154 | private command _insertSchemaMigration pMigrationVersion, pConnectionA 155 | local tSQL, tBindingsA, tRecords 156 | 157 | local theAffectedRows 158 | 159 | put pMigrationVersion + 0 into tBindingsA[1] 160 | 161 | put "UPDATE schema SET version = :1" into tSQL 162 | dbconn_executeWBindings tSQL, tBindingsA, pConnectionA 163 | put it into theAffectedRows 164 | 165 | if theAffectedRows is 0 then 166 | put "INSERT INTO schema (version) VALUES (:1)" into tSQL 167 | dbconn_executeWBindings tSQL, tBindingsA, pConnectionA 168 | end if 169 | 170 | return empty 171 | end _insertSchemaMigration 172 | 173 | 174 | private function _listTables pConnectionA 175 | local tConnID, tTables, tError 176 | 177 | put dbconn_get("connection id", pConnectionA) into tConnID 178 | 179 | if dbconn_get("type", pConnectionA) is "valentina" then 180 | local tTableCount, tTableRef 181 | 182 | put VDatabase_TableCount( tConnID ) into tTableCount 183 | repeat with i = 1 to tTableCount 184 | put VDatabase_Table( tConnID, i) into tTableRef 185 | put VTable_Name( tTableRef) & cr after tTables 186 | end repeat 187 | delete the last char of tTables 188 | put _valentinaErrorSchema(tConnID) into tError 189 | if tError is not empty then 190 | sqlyoga__throwError sqlyoga__errorTypeFromError(pConnectionA, tError), \ 191 | "retrieving database tables:" && tError 192 | end if 193 | else 194 | if dbconn_get("type", pConnectionA) is "mysql" then 195 | put revDataFromQuery(tab, cr, tConnID, "SHOW TABLES") into tTables 196 | else 197 | put revDatabaseTableNames(tConnID) into tTables 198 | end if 199 | if item 1 of tTables is "revdberr" then 200 | sqlyoga__throwError sqlyoga__errorTypeFromError(pConnectionA, item 2 to -1 of tTables), \ 201 | "retrieving database tables:" && item 2 to -1 of tTables 202 | end if 203 | end if 204 | 205 | return tTables for value 206 | end _listTables 207 | 208 | 209 | /** 210 | Summary: Returns the current version number of the database. 211 | 212 | Parameters: 213 | pConnectionA: A connection array. 214 | 215 | Description: 216 | This function returns 0 if the `version` table does not exist. 217 | 218 | Returns: Version or error in result. 219 | */ 220 | private function _getCurrentSchemaVersion pConnectionA 221 | local tSQL, tBindingsA 222 | local tVersion = "0" 223 | 224 | if "schema" is among the lines of _listTables(pConnectionA) then 225 | put "SELECT version FROM schema" into tSQL 226 | put dbconn_retrieveQueryAsData(tSQL, tBindingsA, empty, empty, pConnectionA) into tVersion 227 | end if 228 | 229 | return tVersion for value 230 | end _getCurrentSchemaVersion 231 | 232 | 233 | private command _createSchemaMigrationsTable pConnectionA 234 | local tDBTables, tSQL 235 | 236 | put revDatabaseTableNames(dbconn_get("connection id", pConnectionA)) into tDBTables 237 | if "schema" is not among the lines of tDBTables then 238 | put "CREATE TABLE schema (version varchar(255) NOT NULL UNIQUE)" into tSQL 239 | dbconn_executeSQL tSQL, pConnectionA 240 | end if 241 | 242 | return empty 243 | end _createSchemaMigrationsTable 244 | 245 | 246 | /** 247 | Summary: Updates the physical database schema to the schema version specified. 248 | 249 | Parameters: 250 | pVersion: The version to upgrade the database schema to. 251 | 252 | Description: 253 | DEPRECATED 254 | 255 | Returns: Empty 256 | */ 257 | command dbschema_migrateToVersion pVersion, pConnectionA, pDBKey 258 | local theContainer, theVersions, theSQL 259 | local theError 260 | 261 | put dbobject_createConnectionArray(pConnectionA, pDBKey) into pConnectionA 262 | put dbobject_get("schema migrations", pConnectionA["database"]) into theContainer 263 | 264 | repeat with i = 1 to the number of controls of theContainer 265 | put the short name of button i of theContainer & cr after theVersions 266 | end repeat 267 | delete the last char of theVersions 268 | sort lines of theVersions numeric by word 2 to -1 of each 269 | 270 | dbconn_beginTransaction empty, pConnectionA 271 | 272 | local theConnId, theQuotesA 273 | 274 | put dbconn_get("connection id", pConnectionA) into theConnID 275 | put dbconn_get("quotes") into theQuotesA 276 | 277 | set the wholeMatches to true 278 | if "schema" is not among the lines of revDatabaseTableNames(theConnID) then 279 | put "CREATE TABLE " & theQuotesA["left"] & "schema" & theQuotesA["right"] & " (version integer NOT NULL)" into theSQL 280 | dbconn_executeSQL theSQL, pConnectionA 281 | 282 | put format("INSERT INTO %sschema%s (version) VALUES(0)", theQuotesA["left"], theQuotesA["right"]) into theSQL 283 | dbconn_executeSQL theSQL, pConnectionA 284 | end if 285 | 286 | local theBindingsA, theInstalledVersion 287 | 288 | put format("SELECT version FROM %sschema%s", theQuotesA["left"], theQuotesA["right"]) into theSQL 289 | put empty into theBindingsA 290 | put dbconn_retrieveQueryAsData(theSQL, theBindingsA, tab, cr, pConnectionA) into theInstalledVersion 291 | 292 | local theControlName, theVersion, theLastInstalledVersion 293 | 294 | repeat for each line theControlName in theVersions 295 | put word 2 of theControlName into theVersion 296 | if theVersion > pVersion then exit repeat 297 | 298 | if theVersion > theInstalledVersion then 299 | try 300 | dispatch "InstallSchemaVersion" to button ("Version" && theVersion) of theContainer with pConnectionA 301 | catch theError 302 | end try 303 | end if 304 | 305 | if theError is empty then 306 | put theVersion into theLastInstalledVersion 307 | end if 308 | 309 | if theError is not empty then exit repeat 310 | end repeat 311 | 312 | if theError is empty then 313 | ## Don't update if last installed version < installed version. May be running old version 314 | ## of software and trying to upgrade to latest vesion caused error because 315 | ## table/fields already existed. 316 | if theLastInstalledVersion > theInstalledVersion then 317 | put format("UPDATE %sschema%s SET version = " & theLastInstalledVersion, theQuotesA["left"], theQuotesA["right"]) into theSQL 318 | dbconn_executeSQL theSQL, pConnectionA 319 | end if 320 | end if 321 | 322 | if theError is empty then 323 | dbconn_commitTransaction empty, pConnectionA 324 | else 325 | dbconn_rollbackTransaction empty, pConnectionA 326 | end if 327 | 328 | try 329 | local theType, theDo 330 | 331 | ## Let db clean up 332 | put dbconn_get("type", pConnectionA) into theType 333 | 334 | replace space with empty in theType 335 | put "_" & theType & "_afterMigration pConnectionA" into theDo 336 | do theDo 337 | catch e 338 | sqlyoga__throwError kErrErrorInFunction, "afterMigration has not been defined for database type '" & theType & "'" 339 | end try 340 | 341 | if theError is not empty then 342 | throw theError 343 | end if 344 | 345 | return empty 346 | end dbschema_migrateToVersion 347 | 348 | 349 | ## Creates a table in the database 350 | command dbschema_createTable pName, pTableA, pConnectionA, pDBKey 351 | local theType, theEncoding 352 | 353 | -- pTableA["primary key"] 354 | -- pTableA["fields"] 355 | -- pTableA["foreign keys"] 356 | -- pTableA["indexes"] 357 | put dbobject_createConnectionArray(pConnectionA, pDBKey) into pConnectionA 358 | put dbconn_get("type", pConnectionA) into theType 359 | 360 | ## Get effective encoding 361 | put pTableA["encoding"] into theEncoding 362 | if theEncoding is empty then put dbobject_get("encoding", pConnectionA["database"]) into theEncoding 363 | put theEncoding into pTableA["encoding"] 364 | 365 | ## fill in encoding and check for primary key field 366 | repeat for each key theKey in pTableA["fields"] 367 | if pTableA["fields"][theKey]["encoding"] is empty then 368 | put theEncoding into pTableA["fields"][theKey]["encoding"] 369 | end if 370 | 371 | if pTableA["primary key"] is empty and pTableA["fields"][theKey]["name"] is "id" then 372 | put "id" into pTableA["primary key"] 373 | end if 374 | end repeat 375 | 376 | local theDo 377 | 378 | try 379 | replace space with empty in theType 380 | put "_" & theType & "_dbCreateTable pName, pTableA, pConnectionA" into theDo 381 | do theDo 382 | catch e 383 | sqlyoga__throwError kErrErrorInFunction, param(0) && "has not been defined for database type '" & theType & "'" 384 | end try 385 | 386 | local theIndexA 387 | 388 | repeat with i = 1 to the number of elements of pTableA["indexes"] 389 | put pTableA["indexes"][i] into theIndexA 390 | if theIndexA["table"] is empty then 391 | put pName into theIndexA["table"] 392 | end if 393 | 394 | dbschema_createIndex theIndexA["name"], theIndexA, pConnectionA 395 | end repeat 396 | 397 | return empty 398 | end dbschema_createTable 399 | 400 | 401 | command dbschema_deleteTable pName 402 | 403 | end dbschema_deleteTable 404 | 405 | 406 | command dbschema_createFields pTable, pFieldsA, pConnection, pDBKey 407 | local theConnectionA 408 | local theType, theEncoding 409 | 410 | -- name 411 | -- type 412 | -- length 413 | -- accepts null 414 | -- default value 415 | put dbobject_createConnectionArray(pConnection, pDBKey) into theConnectionA 416 | put dbconn_get("type", theConnectionA) into theType 417 | 418 | ## Get effective encoding 419 | put dbobject_get("encoding", theConnectionA["database"]) into theEncoding 420 | 421 | ## fill in encoding for fields 422 | repeat for each key theKey in pFieldsA 423 | if pFieldsA[theKey]["encoding"] is empty then 424 | put theEncoding into pFieldsA[theKey]["encoding"] 425 | end if 426 | end repeat 427 | 428 | local theDo 429 | 430 | try 431 | replace space with empty in theType 432 | put "_" & theType & "_dbCreateFields pTable, pFieldsA, theConnectionA" into theDo 433 | do theDo 434 | catch e 435 | sqlyoga__throwError kErrErrorInFunction, param(0) && "has not been defined for database type '" & theType & "'" 436 | end try 437 | 438 | # sync schema with table changes 439 | dbschema_tableSet pTable, empty, empty, theConnectionA["database"] 440 | dbsynch_schemaImportTable theConnectionA, pTable 441 | 442 | return empty 443 | end dbschema_createFields 444 | 445 | 446 | command dbschema_deleteFields pTable, pFields, pConnection, pDBKey 447 | local theConnectionA, theType, theDo 448 | 449 | put dbobject_createConnectionArray(pConnection, pDBKey) into theConnectionA 450 | put dbconn_get("type", theConnectionA) into theType 451 | 452 | try 453 | replace space with empty in theType 454 | put "_" & theType & "_dbDeleteFields pTable, pFields, theConnectionA" into theDo 455 | do theDo 456 | catch e 457 | sqlyoga__throwError kErrErrorInFunction, param(0) && "has not been defined for database type '" & theType & "'" 458 | end try 459 | 460 | # Sync schema with table changes 461 | dbschema_tableSet pTable, empty, empty, theConnectionA["database"] 462 | dbsynch_schemaImportTable theConnectionA, pTable 463 | 464 | return empty 465 | end dbschema_deleteFields 466 | 467 | 468 | command dbschema_createIndex pName, pIndexA, pConnection, pDBKey 469 | local theConnectionA, theType, theDo 470 | 471 | -- table 472 | -- type (unique, simple, fulltext, spatial) 473 | -- length 474 | -- fields[1].."name", "length", "sort order", "collation" 475 | -- indexing method (btree, hash, rtree) 476 | 477 | put dbobject_createConnectionArray(pConnection, pDBKey) into theConnectionA 478 | put dbconn_get("type", theConnectionA) into theType 479 | 480 | try 481 | replace space with empty in theType 482 | put "_" & theType & "_dbCreateTableIndexes pName, pIndexA, theConnectionA" into theDo 483 | do theDo 484 | catch e 485 | sqlyoga__throwError kErrErrorInFunction, param(0) && "has not been defined for database type '" & theType & "'" 486 | end try 487 | 488 | return empty 489 | end dbschema_createIndex 490 | 491 | 492 | command dbschema_deleteIndex pIndexName, pTableName, pConnection, pDBKey 493 | local theConnectionA, theType, theDo 494 | 495 | put dbobject_createConnectionArray(pConnection, pDBKey) into theConnectionA 496 | put dbconn_get("type", theConnectionA) into theType 497 | 498 | try 499 | replace space with empty in theType 500 | put "_" & theType & "_dbDeleteIndex pIndexName, pTableName, pConnectionA" into theDo 501 | do theDo 502 | catch e 503 | sqlyoga__throwError kErrErrorInFunction, param(0) && "has not been defined for database type '" & theType & "'" 504 | end try 505 | 506 | return empty 507 | end dbschema_deleteIndex 508 | 509 | 510 | command dbschema_createTriggers pConnection, pDBKey 511 | local theConnectionA, theType, theDo 512 | 513 | put dbobject_createConnectionArray(pConnection, pDBKey) into theConnectionA 514 | put dbconn_get("type", theConnectionA) into theType 515 | 516 | try 517 | replace space with empty in theType 518 | put "_" & theType & "_dbCreateForeignKeyConstraints theConnectionA" into theDo 519 | do theDo 520 | catch e 521 | sqlyoga__throwError kErrErrorInFunction, param(0) && "has not been defined for database type '" & theType & "'" 522 | end try 523 | 524 | return empty 525 | end dbschema_createTriggers 526 | 527 | 528 | --> sql databases 529 | 530 | /** 531 | Summary: Makes any changes necessary to database to put it in synch with the current schema. 532 | 533 | Parameters: 534 | pConnectionA: 535 | pDBKey: 536 | 537 | Description: 538 | 539 | Returns: empty 540 | */ 541 | command dbsynch_databaseWithSchema pConnectionA, pDBKey 542 | local theError, i, theDBKey 543 | local theConnectionID 544 | local theDatabaseTables 545 | local theDBType 546 | local theSQL 547 | local theTable, theTables 548 | local theVersion 549 | local theRecords 550 | 551 | put dbobject_createConnectionArray(pConnectionA, pDBKey) into pConnectionA 552 | 553 | set the wholeMatches to true 554 | dbconn_connect pConnectionA 555 | put it into theConnectionID 556 | 557 | put dbconn_get("type", pConnectionA) into theDBType 558 | put dbconn_get("database", pConnectionA) into theDBKey 559 | 560 | replace space with empty in theDBType 561 | 562 | put revDatabaseTableNames(theConnectionID) into theDatabaseTables 563 | if item 1 of theDatabaseTables is "revdberr" then 564 | sqlyoga__throwError sqlyoga__errorTypeFromError(pConnectionA, item 2 to -1 of theDatabaseTables), \ 565 | "failed to retrieve database table names:" && item 2 to -1 of theDatabaseTables 566 | end if 567 | 568 | ## CREATE OR ALTER TABLES THAT EXIST IN SCHEMA 569 | put dbschema_get("tables", theDBKey) into theTables 570 | 571 | dbconn_beginTransaction empty, pConnectionA 572 | 573 | try 574 | repeat for each line theTable in theTables 575 | if theTable is among the lines of theDatabaseTables then 576 | dbsynch_dbAlterTable theTable, pConnectionA 577 | else 578 | dbsynch_dbCreateTable theTable, pConnectionA 579 | end if 580 | end repeat 581 | 582 | ## ANY REMAINING FOREIGN KEY OPERATIONS (ACTUAL FOREIGN KEYS THROUGH ALTERTABLE, CREATE TRIGGERS, ETC.) 583 | local theDo 584 | 585 | try 586 | put "_" & theDBType & "_dbCreateForeignKeyConstraints pConnectionA" into theDo 587 | do theDo 588 | catch e 589 | sqlyoga__throwError kErrErrorInFunction, param(0) && "has not been defined for database type '" & theDBType & "'" 590 | end try 591 | 592 | ## Update schema table 593 | _createSchemaMigrationsTable pConnectionA 594 | 595 | put dbschema_get("version", theDBKey) into theVersion 596 | 597 | _insertSchemaMigration theVersion, pConnectionA 598 | catch theError 599 | end try 600 | 601 | if theError is empty then 602 | dbconn_commitTransaction empty, pConnectionA 603 | else 604 | dbconn_rollbackTransaction empty, pConnectionA 605 | end if 606 | 607 | return empty 608 | end dbsynch_databaseWithSchema 609 | 610 | 611 | # Puts active schema in synch with database 612 | command dbsynch_schemaWithDatabase pConnectionA, pDBKey 613 | local theConnectionID 614 | local theSchemaTables 615 | local theTable, theTableA 616 | local theTables 617 | 618 | put dbobject_createConnectionArray(pConnectionA, pDBKey) into pConnectionA 619 | 620 | set the wholeMatches to true 621 | 622 | put dbconn_get("connection id", pConnectionA) into theConnectionID 623 | 624 | if theConnectionID < 1 then 625 | sqlyoga__throwError sqlyoga__errorTypeFromError(pConnectionA, "no connection"), "you must connect to the database before importing the schema" 626 | end if 627 | 628 | put _listTables(pConnectionA) into theTables 629 | 630 | dbschema_reset pConnectionA["database"] 631 | 632 | ## Import tables 633 | repeat for each line theTable in theTables 634 | dbsynch_schemaImportTable pConnectionA, theTable 635 | end repeat 636 | 637 | # Set version 638 | dbschema_set "version", _getCurrentSchemaVersion(pConnectionA), pConnectionA["database"] 639 | 640 | return empty 641 | end dbsynch_schemaWithDatabase 642 | 643 | 644 | on dbsynch_dbDropTable pConnectionA, pTable 645 | local theSQL 646 | 647 | put format("DROP TABLE IF EXISTS '%s'", pTable) into theSQL 648 | dbconn_executeSQL theSQL, pConnectionA 649 | 650 | return empty 651 | end dbsynch_dbDropTable 652 | 653 | 654 | command dbsynch_dbCreateTable pTableName, pConnectionA 655 | local theDBType 656 | local theSQL 657 | local theTableA 658 | 659 | ## NO FIELDS, NO CREATE 660 | if dbschema_tableGet(pTableName, "fields", pConnectionA["database"]) is empty then return empty 661 | 662 | -- put dbconn_get("type", pConnectionA) into theDBType 663 | -- replace space with empty in theDBType 664 | 665 | ## CREATE TABLE 666 | put dbsynch_createTableSQL(pTableName, pConnectionA) into theSQL 667 | 668 | dbconn_executeSQL theSQL, pConnectionA 669 | 670 | -- ## INDEXES 671 | -- local theDo 672 | 673 | -- put "_" & theDBType & "_dbCreateTableIndexes pConnectionA, pTableA" into theDo 674 | -- do theDo 675 | 676 | return empty 677 | end dbsynch_dbCreateTable 678 | 679 | 680 | function dbsynch_createTableSQL pTableName, pConnectionA 681 | local theDBType, theDo 682 | local theTableA, theSQL, theHandler 683 | 684 | if dbschema_tableGet(pTableName, "fields", pConnectionA["database"]) is empty then 685 | sqlyoga__throwError kErrErrorInFunction, param(0) && pTableName && "does not have any fields" 686 | end if 687 | 688 | put dbschema_tableGet(pTableName,, pConnectionA["database"]) into theTableA 689 | 690 | try 691 | put dbconn_get("type", pConnectionA) into theDBType 692 | replace space with empty in theDBType 693 | put "_" & theDBType & "_dbCreateTableSQL" into theHandler 694 | put "put" && theHandler && "(pTableName, theTableA, pConnectionA) into theSQL" into theDo 695 | do theDo 696 | catch e 697 | sqlyoga__throwError kErrErrorInFunction, param(0) && "has not been defined for database type '" & theDBType & "'" 698 | end try 699 | 700 | return theSQL 701 | end dbsynch_createTableSQL 702 | 703 | 704 | command dbsynch_dbAlterTable pTableName, pConnectionA 705 | local theDBType, theDo, theHandler 706 | local theTableA 707 | 708 | if dbschema_tableGet(pTableName, "fields", pConnectionA["database"]) is empty then 709 | sqlyoga__throwError kErrErrorInFunction, param(0) && pTableName && "does not have any fields" 710 | end if 711 | 712 | put dbschema_tableGet(pTableName,, pConnectionA["database"]) into theTableA 713 | 714 | try 715 | put dbconn_get("type", pConnectionA) into theDBType 716 | replace space with empty in theDBType 717 | put "_" & theDBType & "_dbAlterTable" into theHandler 718 | put theHandler && "pTableName, theTableA, pConnectionA" into theDo 719 | do theDo 720 | catch e 721 | sqlyoga__throwError kErrErrorInFunction, param(0) && "has not been defined for database type '" & theDBType & "'" 722 | end try 723 | 724 | return empty 725 | end dbsynch_dbAlterTable 726 | 727 | 728 | command dbsynch_schemaImportTable pConnectionA, pTable 729 | local i 730 | local theDBType, theSystemTables, theDo 731 | local theField, theFieldA, theFields, theFieldsA, theIndexFields 732 | local theIndex, theIndexA, theIndexName 733 | local theIndexesA 734 | local theOldFieldsA, theOldIndexesA 735 | local theRelationshipsA 736 | local theTableA 737 | 738 | put dbconn_get("type", pConnectionA) into theDBType 739 | replace space with empty in theDBType 740 | 741 | ## FILTER OUT DB CREATED TABLES 742 | set the wholeMatches to true 743 | 744 | try 745 | put "put _" & theDBType & "_systemTables() into theSystemTables" into theDo 746 | do theDo 747 | put the result into theSystemTables 748 | catch e 749 | sqlyoga__throwError kErrErrorInFunction, "systemTables() has not been defined for database type '" & theDBType & "'" 750 | end try 751 | 752 | if pTable is among the lines of theSystemTables then 753 | return empty 754 | end if 755 | 756 | --> theArrayA["fields"] 757 | --> theArrayA["fields"][index]["length"] (string, text, binary, integer) 758 | --> theArrayA["fields"][index]["precision"] (decimal) 759 | --> theArrayA["fields"][index]["scale"] (decimal) 760 | --> theArrayA["fields"][index]["signed"] 761 | --> theArrayA["fields"][index]["primary key"] 762 | --> theArrayA["fields"][index]["fixed width"] (for string, specifies varchar or char) 763 | --> theArrayA["fields"][index]["default value"] 764 | --> theArrayA["fields"][index]["unique"] 765 | --> theArrayA["fields"][index]["accepts null"] 766 | try 767 | put "_" & theDBType & "_dbTableFields pConnectionA, pTable" into theDo 768 | do theDo 769 | put it into theTableA 770 | catch e 771 | sqlyoga__throwError kErrErrorInFunction, "dbTableFields has not been defined for database type '" & theDBType & "'" 772 | end try 773 | 774 | try 775 | put "_" & theDBType & "_dbTableIndexes pConnectionA, pTable, theFieldsA " into theDo 776 | do theDo 777 | put it into theIndexesA 778 | catch e 779 | sqlyoga__throwError kErrErrorInFunction, "dbTableIndexes has not been defined for database type '" & theDBType & "'" 780 | end try 781 | 782 | set the wholeMatches to true 783 | 784 | ## Assign meta type to fields 785 | repeat for each key theField in theTableA["fields"] 786 | put _NormalizeFieldType(theTableA["fields"][theField]["type"]) into theTableA["fields"][theField]["meta type"] 787 | end REPEAT 788 | 789 | ## Update list of primary key fields if need be. Multiple primary keys 790 | ## are stored as an index. 791 | repeat for each key theIndexName in theIndexesA["indexes"] 792 | if theIndexesA["indexes"][theIndexName]["primary key"] then 793 | put theIndexesA["indexes"][theIndexName]["fields"] into theFields 794 | split theFields by column 795 | replace cr with comma in theFields[1] 796 | put theFields[1] into theTableA["primary key"] 797 | exit repeat 798 | end if 799 | end repeat 800 | 801 | dbschema_addTableDefinition pTable, theTableA, pConnectionA["database"] 802 | 803 | return empty 804 | end dbsynch_schemaImportTable 805 | 806 | 807 | -- splits a field type into type and length 808 | -- values come out in parameters 809 | command dbsynch_splitFieldType pFieldType, @rType, @rLength, @rSigned 810 | local theOffset 811 | 812 | put empty into rType 813 | put empty into rLength 814 | put empty into rSigned 815 | 816 | put offset("(", pFieldType) into theOffset 817 | if theOffset > 0 then 818 | put char 1 to theOffset - 1 of pFieldType into rType 819 | put char theOffset + 1 to offset(")", pFieldType) - 1 of pFieldType into rLength 820 | else 821 | put word 1 of pFieldType into rType 822 | end if 823 | put word 2 of pFieldType is "signed" into rSigned 824 | 825 | return empty 826 | end dbsynch_splitFieldType 827 | 828 | 829 | command dbsynch_dbTableCreateSQL pConnectionA, pTable 830 | local theColumn 831 | local theConnectionID 832 | local theCreateTableSQL 833 | local theCursor 834 | local theQs,theSQL 835 | local theType 836 | 837 | put dbconn_get("connection id", pConnectionA) into theConnectionID 838 | put dbconn_get("type", pConnectionA) into theType 839 | put dbconn_get("quotes", pConnectionA) into theQs 840 | 841 | put theQs["left"] & pTable & theQs["right"] into pTable 842 | 843 | switch theType 844 | case "mysql" 845 | put "show create table" && pTable into theSQL 846 | put "Create Table" into theColumn 847 | break 848 | case "sqlite" 849 | put "SELECT sql FROM sqlite_master WHERE name =" && pTable & " AND type = 'table'" into theSQL 850 | put "sql" into theColumn 851 | break 852 | case "valentina" 853 | 854 | break 855 | case "postgresql" 856 | 857 | break 858 | case "sql server" 859 | 860 | break 861 | end SWITCH 862 | 863 | put revQueryDatabase(theConnectionID, theSQL) into theCursor 864 | if theCursor is an integer then 865 | put revDatabaseColumnNamed(theCursor, theColumn) into theCreateTableSQL 866 | revCloseCursor theCursor 867 | else 868 | sqlyoga__throwError sqlyoga__errorTypeFromError(pConnectionA, theCursor), "unable to retrieve create table SQL from database" 869 | end if 870 | 871 | return theCreateTableSQL for value 872 | end dbsynch_dbTableCreateSQL 873 | 874 | 875 | --> Driver: SQLite 876 | 877 | 878 | private command _sqlite_dbTableFields pConnectionA, pTable 879 | local leftQ,rightQ,theA 880 | local theConnectionID 881 | local theCursor 882 | local theField 883 | local thePrimaryKeys 884 | local theQuotesA 885 | local theSQL 886 | 887 | put dbconn_get("connection id", pConnectionA) into theConnectionID 888 | put dbconn_get("quotes", pConnectionA) into theQuotesA 889 | put theQuotesA["left"] into leftQ 890 | put theQuotesA["right"] into rightQ 891 | 892 | put "PRAGMA table_info(" & leftQ & pTable & rightQ & ")" into theSQL 893 | put revQueryDatabase(theConnectionID, theSQL) into theCursor 894 | if theCursor is not an integer then 895 | sqlyoga__throwError sqlyoga__errorTypeFromError(pConnectionA, theCursor), "retrieving table fields" 896 | end if 897 | 898 | if revQueryIsAtEnd(theCursor) then 899 | revCloseCursor theCursor 900 | sqlyoga__throwError sqlyoga__errorTypeFromError(pConnectionA, "no fields"), \ 901 | "no fields were returned for table '" & pTable & "'" 902 | end if 903 | 904 | -- Returns cid, name, type, notnull, dflt_value, pk 905 | repeat forever 906 | put revDatabaseColumnNamed(theCursor, "name") into theField 907 | 908 | put theField & comma after theA["field order"] 909 | 910 | put theField into theA["fields"][theField]["name"] 911 | put empty into theA["fields"][theField]["encoding"] 912 | put revDatabaseColumnNamed(theCursor, "dflt_value") into theA["fields"][theField]["default value"] 913 | ## An empty string would have a ''. Default to NULL 914 | if theA["fields"][theField]["default value"] is empty then put "NULL" into theA["fields"][theField]["default value"] 915 | put revDatabaseColumnNamed(theCursor, "notnull") is "0" into theA["fields"][theField]["accepts null"] # value is 99 for null 916 | put revDatabaseColumnNamed(theCursor, "pk") > 0 into theA["fields"][theField]["primary key"] # value is 0 for not primary 917 | put false into theA["fields"][theField]["unique"] 918 | if theA["fields"][theField]["primary key"] then 919 | put theField & comma after thePrimaryKeys 920 | end if 921 | 922 | ## Note: default value is wrapped in '' if a string. Or it could be something like CURRENT_DATE if it is a sqlite function. 923 | local theType, theLength, isSigned 924 | 925 | dbsynch_splitFieldType revDatabaseColumnNamed(theCursor, "Type"), theType, theLength, isSigned 926 | 927 | put theType into theA["fields"][theField]["type"] 928 | put theLength into theA["fields"][theField]["length"] 929 | put isSigned into theA["fields"][theField]["signed"] 930 | 931 | revMoveToNextRecord theCursor 932 | if the result is not true then exit REPEAT 933 | end repeat 934 | 935 | delete the last char of thePrimaryKeys 936 | put thePrimaryKeys into theA["primary key"] 937 | delete the last char of theA["field order"] 938 | 939 | ## INTEGER PRIMARY KEY IS AN AUTOINCREMENT IN SQLITE 940 | ## ALTHOUGH IT WILL REUSE NUMBERS 941 | ## AUTOINCREMENT KEYWORD MEANS THE TABLE WILL MAX OUT 942 | if the number of items of thePrimaryKeys is 1 then 943 | if theA["fields"][thePrimaryKeys]["type"] is "integer" then 944 | put "sequence" into theA["fields"][thePrimaryKeys]["type"] 945 | put empty into theA["fields"][thePrimaryKeys]["length"] 946 | put false into theA["fields"][thePrimaryKeys]["signed"] 947 | put true into theA["fields"][thePrimaryKeys]["unique"] # primary has to be unique 948 | end if 949 | end if 950 | 951 | revCloseCursor theCursor 952 | 953 | return theA for value 954 | end _sqlite_dbTableFields 955 | 956 | 957 | private command _sqlite_dbTableRelationships pConnectionA, pTable 958 | local theConnectionID, theQuotesA, leftQ, rightQ 959 | local theSQL, theCursor 960 | 961 | put dbconn_get("connection id", pConnectionA) into theConnectionID 962 | put dbconn_get("quotes", pConnectionA) into theQuotesA 963 | put theQuotesA["left"] into leftQ 964 | put theQuotesA["right"] into rightQ 965 | 966 | -- create array of relationships the table has with other tables 967 | put "PRAGMA foreign_key_list(" & leftQ & pTable & rightQ & ")" into theSQL 968 | put revQueryDatabase(theConnectionID, theSQL) into theCursor 969 | if theCursor is not an integer then 970 | sqlyoga__throwError sqlyoga__errorTypeFromError(pConnectionA, theCursor), "retrieving foreign key list" && theCursor 971 | end if 972 | 973 | if not revQueryIsAtEnd(theCursor) then 974 | local theIndex, theReferencedTable, theSourceColumns, theReferencedColumns, theA 975 | 976 | repeat forever 977 | add 1 to theIndex 978 | 979 | put revDatabaseColumnNamed(theCursor, "table") into theReferencedTable 980 | put revDatabaseColumnNamed(theCursor, "from") into theSourceColumns 981 | put revDatabaseColumnNamed(theCursor, "to") into theReferencedColumns 982 | 983 | put theSourceColumns into theA[theIndex]["from key"] 984 | put theReferencedTable into theA[theIndex]["to table"] 985 | put theReferencedColumns into theA[theIndex]["to key"] 986 | 987 | revMoveToNextRecord theCursor 988 | if the result is not true then exit repeat 989 | end repeat 990 | end if 991 | revCloseCursor theCursor 992 | 993 | return theA for value 994 | end _sqlite_dbTableRelationships 995 | 996 | 997 | private command _sqlite_dbTableIndexes pConnectionA, pTable, pFieldsA 998 | local leftQ,rightQ 999 | local theA,theSQL 1000 | local theConnectionID 1001 | local theCursor, theCursor2 1002 | local theFieldName 1003 | local theIndexName 1004 | local theQuotesA 1005 | local thisIsPrimaryKeyIndex 1006 | 1007 | put dbconn_get("connection id", pConnectionA) into theConnectionID 1008 | put dbconn_get("quotes", pConnectionA) into theQuotesA 1009 | put theQuotesA["left"] into leftQ 1010 | put theQuotesA["right"] into rightQ 1011 | 1012 | put "PRAGMA index_list(" & leftQ & pTable & rightQ & ")" into theSQL 1013 | put revQueryDatabase(theConnectionID, theSQL) into theCursor 1014 | if theCursor is empty then put "no indexes for table" && pTable into theCursor 1015 | if theCursor is not an integer then 1016 | sqlyoga__throwError sqlyoga__errorTypeFromError(pConnectionA, theCursor), "retrieving index list" && theCursor 1017 | end if 1018 | 1019 | if not revQueryIsAtEnd(theCursor) then 1020 | repeat forever 1021 | put revDatabaseColumnNamed(theCursor, "name") into theIndexName 1022 | put revDatabaseColumnNamed(theCursor, "unique") is 1 into theA["indexes"][theIndexName]["unique"] 1023 | put true into thisIsPrimaryKeyIndex 1024 | 1025 | put "PRAGMA index_info(" & theIndexName & ")" into theSQL 1026 | put revQueryDatabase( theConnectionID, theSQL) into theCursor2 1027 | if not revQueryIsAtEnd(theCursor2) then 1028 | repeat forever 1029 | put revDatabaseColumnNamed(theCursor2, "name") into theFieldName 1030 | put theFieldName into line (the number of lines of theA["indexes"][theIndexName]["fields"] + 1) \ 1031 | of theA["indexes"][theIndexName]["fields"] 1032 | 1033 | if not pFieldsA["fields"][theFieldName]["primary key"] then put false into thisIsPrimaryKeyIndex 1034 | 1035 | revMoveToNextRecord theCursor2 1036 | if the result is not true then exit repeat 1037 | end repeat 1038 | end if 1039 | 1040 | revCloseCursor theCursor2 1041 | 1042 | ## TODO: ADD CHECK FOR FULL TEXT 1043 | if theA["indexes"][theIndexName]["unique"] then 1044 | put "unique" into theA["indexes"][theIndexName]["style"] 1045 | else 1046 | put "simple" into theA["indexes"][theIndexName]["style"] 1047 | end if 1048 | 1049 | put thisIsPrimaryKeyIndex into theA["indexes"][theIndexName]["primary key"] 1050 | revMoveToNextRecord theCursor 1051 | if the result is not true then exit REPEAT 1052 | end repeat 1053 | end if 1054 | 1055 | revCloseCursor theCursor 1056 | 1057 | return theA for value 1058 | end _sqlite_dbTableIndexes 1059 | 1060 | 1061 | private function _sqlite_getFieldSQLType pFieldA 1062 | local theType, theLimit, thePrecision, theScale, theSQLType 1063 | 1064 | put _effectiveFieldtype(pFieldA) into theType 1065 | put pFieldA["length"] into theLimit 1066 | put pFieldA["precision"] into thePrecision 1067 | put pFieldA["scale"] into theScale 1068 | 1069 | put toupper(theType) into theSQLType 1070 | 1071 | switch theType 1072 | case "text" 1073 | if pFieldA["fixed width"] then 1074 | put "CHAR" into theSQLType 1075 | if theLimit is not an integer then 1076 | put 255 into theLimit 1077 | end if 1078 | end if 1079 | 1080 | if theLimit is an integer then 1081 | if theLimit <= 255 then 1082 | if theSQLType is "text" then 1083 | put "VARCHAR" into theSQLType 1084 | end if 1085 | end if 1086 | 1087 | put theSQLType & "(" & theLimit & ")" into theSQLType 1088 | end if 1089 | break 1090 | 1091 | case "float" 1092 | put "REAL" into theSQLType 1093 | break 1094 | 1095 | case "double precision" 1096 | put "DOUBLE PRECISION" into theSQLType 1097 | break 1098 | 1099 | case "decimal" 1100 | if thePrecision is not an integer then 1101 | put "10" into thePrecision 1102 | end if 1103 | if theScale is not an integer then 1104 | put "2" into theScale 1105 | end if 1106 | put "DECIMAL (" & thePrecision & comma & theScale & ")" into theSQLType 1107 | break 1108 | 1109 | case "date time" 1110 | put "DATETIME" into theSQLType 1111 | break 1112 | 1113 | case "binary" 1114 | put "BLOB" into theSQLType 1115 | break 1116 | end switch 1117 | 1118 | return theSQLType 1119 | end _sqlite_getFieldSQLType 1120 | 1121 | 1122 | private function _sqlite_getFieldCollateType pFieldA 1123 | local tCollation 1124 | 1125 | if pFieldA["charset"] is not empty then 1126 | if pFieldA["charset"] is "binary" then 1127 | put "BINARY" into tCollation 1128 | else 1129 | put "NOCASE" into tCollation 1130 | end if 1131 | else 1132 | if pFieldA["type"] is "text" then 1133 | put "NOCASE" into tCollation 1134 | end if 1135 | end if 1136 | 1137 | return tCollation 1138 | end _sqlite_getFieldCollateType 1139 | 1140 | 1141 | private command _sqlite_dbCreateTable pName, pTableA, pConnectionA 1142 | local theSQL 1143 | 1144 | put _sqlite_dbCreateTableSQL(pName, pTableA, pConnectionA) into theSQL 1145 | dbconn_executeSQL theSQL, pConnectionA 1146 | 1147 | return empty 1148 | end _sqlite_dbCreateTable 1149 | 1150 | 1151 | private function _sqlite_dbCreateTableSQL pName, pTableA, pConnectionA 1152 | local theQsA, theSQL, theField 1153 | 1154 | put dbconn_get("quotes", pConnectionA) into theQsA 1155 | 1156 | put "CREATE TABLE" && theQsA["left"] & pName & theQsA["right"] into theSQL 1157 | put false into pTableA["@primary key is taken care of"] 1158 | 1159 | ## Fields 1160 | put " (" after theSQL 1161 | repeat for each item theField in pTableA["field order"] 1162 | put _sqlite_getColumnDefSQL(pTableA, pTableA["fields"][theField]) after theSQL 1163 | put comma & space after theSQL 1164 | end repeat 1165 | 1166 | ## Primary key 1167 | if pTableA["primary key"] is not empty and not pTableA["@primary key is taken care of"] then 1168 | put " PRIMARY KEY(" & pTableA["primary key"] & "), " after theSQL 1169 | end if 1170 | 1171 | ## Foreign keys 1172 | if the keys of pTableA["foreign keys"] is not empty then 1173 | put _sqlite_ForeignKeySQL(pTableA["foreign keys"], pConnectionA) after theSQL 1174 | put ")" after theSQL 1175 | else 1176 | put ")" into char -2 to -1 of theSQL 1177 | end if 1178 | 1179 | return theSQL 1180 | end _sqlite_dbCreateTableSQL 1181 | 1182 | 1183 | -- pTableA["@primary key is taken care of"] can be modified 1184 | function _sqlite_getColumnDefSQL @pTableA, pFieldA 1185 | local theQsA, theSQLType, theCollation, theSQL 1186 | 1187 | put quote into theQsA["left"] 1188 | put quote into theQsA["right"] 1189 | put _sqlite_getFieldSQLType(pFieldA) into theSQLType 1190 | put _sqlite_getFieldCollateType(pFieldA) into theCollation 1191 | 1192 | ## Name 1193 | put theQsA["left"] & pFieldA["name"] & theQsA["right"] & space into theSQL 1194 | 1195 | if pFieldA["type"] is "sequence" \ 1196 | and (pTableA["primary key"] is pFieldA["name"] or pTableA["primary key"] is empty) then 1197 | put "INTEGER PRIMARY KEY AUTOINCREMENT " after theSQL 1198 | put pFieldA["name"] into pTableA["primary key"] 1199 | put true into pTableA["@primary key is taken care of"] 1200 | else 1201 | put theSQLType & space after theSQL 1202 | 1203 | if "default" is among the keys of pFieldA then 1204 | switch pFieldA["type"] 1205 | case "boolean" 1206 | if pFieldA["default"] is among the items of "0,false" then put "0" into pFieldA["default"] 1207 | else if pFieldA["default"] is among the items of "1,true" then put "1" into pFieldA["default"] 1208 | break 1209 | end switch 1210 | put "DEFAULT " & pFieldA["default"] & " " after theSQL 1211 | end if 1212 | 1213 | if theCollation is not empty then 1214 | put "COLLATE" && theCollation & " " after theSQL 1215 | end if 1216 | 1217 | if pFieldA["accepts null"] is false then 1218 | put "NOT NULL " after theSQL 1219 | end if 1220 | end if 1221 | delete the last char of theSQL 1222 | return theSQL 1223 | end _sqlite_getColumnDefSQL 1224 | 1225 | 1226 | private function _sqlite_ForeignKeySQL pForeignKeysA, pConnectionA 1227 | local theSQL, theQsA 1228 | 1229 | put dbconn_get("quotes", pConnectionA) into theQsA 1230 | 1231 | -- sqlyoga_addToArray theTableA["foreign keys"], "key:step_id", "references:steps", "on fields:id", "on delete:cascade", "on update:cascade" 1232 | repeat with i = 1 to the number of elements of pForeignKeysA 1233 | if pForeignKeysA[i]["name"] is not empty then 1234 | put "CONSTRAINT " & pForeignKeysA[i]["name"] & " " after theSQL 1235 | end if 1236 | 1237 | put "FOREIGN KEY (" & pForeignKeysA[i]["key"] & ") " & \ 1238 | "REFERENCES " & pForeignKeysA[i]["references"] & "(" & pForeignKeysA[i]["on fields"] & ")" after theSQL 1239 | 1240 | repeat for each item theItem in "on delete,on update" 1241 | if pForeignKeysA[i][theItem] is not empty then 1242 | put " " & toUpper(theItem) && pForeignKeysA[i][theItem] after theSQL 1243 | end if 1244 | end repeat 1245 | 1246 | put ", " after theSQL 1247 | end repeat 1248 | delete char -2 to -1 of theSQL 1249 | 1250 | return theSQL 1251 | end _sqlite_ForeignKeySQL 1252 | 1253 | 1254 | private command _sqlite_dbCreateFields pTable, pFieldsA, pConnectionA 1255 | local theQsA, theTableA, theBaseSQL, theSQL 1256 | 1257 | put dbconn_get("quotes", pConnectionA) into theQsA 1258 | 1259 | put empty into theTableA["primary key"] 1260 | put true into theTableA["@primary key is taken care of"] 1261 | 1262 | put "ALTER TABLE " & theQsA["left"] & pTable & theQsA["right"] & " " into theBaseSQL 1263 | 1264 | repeat with i = 1 to the number of elements of pFieldsA 1265 | put theBaseSQL & "ADD COLUMN " & _sqlite_getColumnDefSQL(theTableA, pFieldsA[i]) into theSQL 1266 | dbconn_executeSQL theSQL, pConnectionA 1267 | end repeat 1268 | 1269 | return empty 1270 | end _sqlite_dbCreateFields 1271 | 1272 | 1273 | private command _sqlite_afterMigration pConnectionA 1274 | dbconn_executeSQL "VACUUM", pConnectionA 1275 | 1276 | return empty 1277 | end _sqlite_afterMigration 1278 | 1279 | 1280 | private command _sqlite_dbDeleteFields pTable, pFields, pConnectionA 1281 | local theQsA 1282 | local theOldCreateSQL, theTempTable 1283 | local theTriggersA, theIndexesA, theOldFields, theNewFields, theItemNo 1284 | local theSQL, theBindingsA, theCreateTableSQL 1285 | 1286 | -- CREATE TABLE 1287 | -- INSERT INTO new table (select from old table) 1288 | -- DROP ORIGINAL TABLE 1289 | -- ALTER NAME OF NEW TABLE 1290 | 1291 | put dbconn_get("quotes", pConnectionA) into theQsA 1292 | put "_tmp_" & pTable & "_tmp_" into theTempTable 1293 | 1294 | ## Preflight) Get current and new field names 1295 | put revDatabaseColumnNames( dbconn_get("connection id", pConnectionA), pTable) into theOldFields 1296 | put theOldFields into theNewFields 1297 | 1298 | set the wholeMatches to true 1299 | repeat for each item theField in pFields 1300 | put itemOffset(theField, theNewFields) into theItemNo 1301 | if theItemNo > 0 then 1302 | delete item theItemNo of theNewFields 1303 | end if 1304 | end repeat 1305 | 1306 | ## 1) Grab triggers, indexes from existing table 1307 | put format("SELECT sql FROM sqlite_master WHERE type = 'trigger' AND tbl_name = '%s'", pTable) into theSQL 1308 | dbconn_retrieveQueryAsArray theSQL,, theBindingsA, theTriggersA, pConnectionA 1309 | 1310 | put format("SELECT sql FROM sqlite_master WHERE type = 'index' AND tbl_name = '%s'", pTable) into theSQL 1311 | dbconn_retrieveQueryAsArray theSQL,, theBindingsA, theIndexesA, pConnectionA 1312 | 1313 | ## 2) Get Create table SQL 1314 | put "SELECT sql FROM sqlite_master WHERE type = 'table' AND tbl_name=" & theQsA["left"] & pTable & theQsA["right"] into theSQL 1315 | put dbconn_retrieveQueryAsData(theSQL, empty, empty, empty, pConnectionA) into theCreateTableSQL 1316 | 1317 | ## 2) Rename existing table. 1318 | put format("ALTER TABLE %s%s%s RENAME TO %s%s%s", theQsA["left"], pTable, theQsA["right"], \ 1319 | theQsA["left"], theTempTable, theQsA["right"]) into theSQL 1320 | dbconn_executeSQL theSQL, pConnectionA 1321 | 1322 | ## 3) Create new table 1323 | local theDefsA 1324 | 1325 | _getAllDefinitionsFromCreateSQL theCreateTableSQL 1326 | 1327 | put it into theDefsA 1328 | 1329 | local theColumnName, theCharNo, theCharNo2 1330 | 1331 | repeat with i = the number of elements of theDefsA down to 1 1332 | put empty into theColumnName 1333 | 1334 | ## Get rid of foreign key refs 1335 | if word 1 to 2 of theDefsA[i]["definition"] is "FOREIGN KEY" then 1336 | put offset("(", theDefsA[i]["definition"]) into theCharNo 1337 | if theCharNo > 0 then 1338 | put offset(")", theDefsA[i]["definition"]) into theCharNo2 1339 | if theCharNo2 > 0 then 1340 | put char theCharNo + 1 to theCharNo2 - 1 of theDefsA[i]["definition"] into theColumnName 1341 | end if 1342 | end if 1343 | else if 1 is 0 then 1344 | ## todo: check for indexes on column 1345 | else 1346 | ## Regular column 1347 | put word 1 of theDefsA[i]["definition"] into theColumnName 1348 | end if 1349 | 1350 | replace "'" with empty in theColumnName 1351 | replace quote with empty in theColumnName 1352 | replace "`" with empty in theColumnName 1353 | 1354 | if theColumnName is among the items of pFields then 1355 | delete char theDefsA[i]["definition start char"] to theDefsA[i]["last char"] of theCreateTableSQL 1356 | end if 1357 | end repeat 1358 | 1359 | dbconn_executeSQL theCreateTableSQL, pConnectionA 1360 | 1361 | ## 4) Copy data over to new table 1362 | -- INSERT INTO t1 SELECT a,b FROM t1_backup 1363 | put format("INSERT INTO %s%s%s (rowid,%s) SELECT rowid,%s FROM %s%s%s", theQsA["left"], pTable, theQsA["right"], \ 1364 | theNewFields, theNewFields, \ 1365 | theQsA["left"], theTempTable, theQsA["right"]) into theSQL 1366 | dbconn_executeSQL theSQL, pConnectionA 1367 | 1368 | ## 5) Delete tmp table 1369 | put format("DROP TABLE %s%s%s", theQsA["left"], theTempTable, theQsA["right"]) into theSQL 1370 | dbconn_executeSQL theSQL, pConnectionA 1371 | 1372 | ## 6) Assign indexes and triggers 1373 | repeat with i = 1 to the number of elements of theIndexesA 1374 | if theIndexesA[i]["sql"] is not empty then 1375 | dbconn_executeSQL theIndexesA[i]["sql"], pConnectionA 1376 | end if 1377 | end repeat 1378 | 1379 | repeat with i = 1 to the number of elements of theTriggersA 1380 | if theTriggersA[i]["sql"] is not empty then 1381 | dbconn_executeSQL theTriggersA[i]["sql"], pConnectionA 1382 | end if 1383 | end repeat 1384 | 1385 | return empty 1386 | end _sqlite_dbDeleteFields 1387 | 1388 | 1389 | command _sqlite_createSQLFromDB pTable, pCreateAs, pExcludeFields, pConnectionA 1390 | local theSQL 1391 | local theTrailer, theQsA, theCreateSQL, theCreateTableSQL 1392 | local theForeignKeysA, theTableA 1393 | 1394 | if pCreateAs is empty then put pTable into pCreateAs 1395 | put dbconn_get("quotes", pConnectionA) into theQsA 1396 | 1397 | put "SELECT sql FROM sqlite_master WHERE type = 'table' AND tbl_name=" & theQsA["left"] & pTable & theQsA["right"] into theSQL 1398 | put dbconn_retrieveQueryAsData(theCreateSQL, empty, empty, empty, pConnectionA) into theCreateTableSQL 1399 | 1400 | set the wholeMatches to true 1401 | 1402 | ## Table columns 1403 | put "PRAGMA table_info(" & theQsA["left"] & pTable & theQsA["right"] & ")" into theSQL 1404 | dbconn_retrieveQueryAsArray theSQL,,, theTableA, pConnectionA 1405 | 1406 | ## Foreign keys 1407 | ## Need to dig into the create SQL for this 1408 | put "PRAGMA foreign_key_list(" & theQsA["left"] & theQsA["right"] & quote & ")" into theSQL 1409 | dbconn_retrieveQueryAsArray theSQL,,, theForeignKeysA, pConnectionA 1410 | 1411 | put "CREATE TABLE " & theQsA["left"] & pCreateAs & theQsA["right"] & " (" into theSQL 1412 | 1413 | ## Column Definitions 1414 | repeat with i = 1 to the number of elements of theTableA 1415 | if theTableA[i]["name"] is among the items of pExcludeFields then next repeat 1416 | 1417 | put theTrailer after theSQL 1418 | 1419 | put quote & theTableA[i]["name"] & quote && theTableA[i]["type"] after theSQL 1420 | if theTableA[i]["pk"] is "1" then 1421 | ## is it autoincrementing? 1422 | ## check create sql to see if AUTOINCREMENT appears. 1423 | ## Note: you might be tempted to check the sqlite_sequence table for an entry but 1424 | ## there will be no entry if no records have been added yet. 1425 | put "SELECT sql FROM sqlite_master WHERE type = 'table' AND tbl_name = " & theQsA["left"] & pTable & theQsA["right"] into theCreateSQL 1426 | put dbconn_retrieveQueryAsData(theCreateSQL, empty, empty, empty, pConnectionA) into theCreateTableSQL 1427 | if theCreateTableSQL contains "autoincrement" then 1428 | put " PRIMARY KEY AUTOINCREMENT" after theSQL 1429 | else 1430 | put " PRIMARY KEY" after theSQL 1431 | if theTableA[i]["notnull"] > 0 then 1432 | put " NOT NULL" after theSQL 1433 | end if 1434 | if theTableA[i]["dflt_value"] is not empty then 1435 | put " DEFAULT " & theTableA[i]["dflt_value"] after theSQL 1436 | end if 1437 | end if 1438 | 1439 | else ## not primary key 1440 | if theTableA[i]["notnull"] > 0 then 1441 | put " NOT NULL" after theSQL 1442 | end if 1443 | if theTableA[i]["dflt_value"] is not empty then 1444 | put " DEFAULT " & theTableA[i]["dflt_value"] after theSQL 1445 | end if 1446 | end if 1447 | 1448 | put ", " into theTrailer 1449 | end repeat 1450 | 1451 | put ")" after theSQL 1452 | 1453 | return theSQL for value 1454 | end _sqlite_createSQLFromDB 1455 | 1456 | 1457 | private command _getAllDefinitionsFromCreateSQL pSQL 1458 | local theDefsA, theDefinition, theDefStartCharNo, theDefEndCharNo, theLastCharNo 1459 | local i = 0 1460 | 1461 | repeat forever 1462 | add 1 to i 1463 | put empty into theDefinition 1464 | 1465 | _extractDefinitionFromCreateSQL pSQL, i, theDefinition, theDefStartCharNo, theDefEndCharNo, theLastCharNo 1466 | if theDefinition is empty then exit repeat 1467 | 1468 | put theDefinition into theDefsA[i]["definition"] 1469 | put theDefStartCharNo into theDefsA[i]["definition start char"] 1470 | put theDefEndCharNo into theDefsA[i]["definition end char"] 1471 | put theLastCharNo into theDefsA[i]["last char"] 1472 | end repeat 1473 | 1474 | return theDefsA for value 1475 | end _getAllDefinitionsFromCreateSQL 1476 | 1477 | 1478 | private command _extractDefinitionFromCreateSQL pSQL, pDefNum, @rCreateDef, @rStartDefCharNo, @rEndDefCharNo, @rClosingCharNo 1479 | local theRootSearchCharNo, theCreateDef 1480 | 1481 | put offset("(", pSQL) into theRootSearchCharNo 1482 | if theRootSearchCharNo is 0 then 1483 | sqlyoga__throwError kErrCantFindObject, "invalid sql statement: missing " & quote & "(" & quote 1484 | end if 1485 | 1486 | add 1 to theRootSearchCharNo 1487 | repeat pDefNum times 1488 | ## root char no will be updated each time 1489 | put _findNextCreateDef(pSQL, theRootSearchCharNo, rStartDefCharNo, rEndDefCharNo) into rCreateDef 1490 | put theRootSearchCharNo into rClosingCharNo 1491 | add 1 to theRootSearchCharNo 1492 | end repeat 1493 | 1494 | return empty 1495 | end _extractDefinitionFromCreateSQL 1496 | 1497 | 1498 | private function _findNextCreateDef pSQL, @xRootSearchCharNo, @rStartCharNo, @rEndCharNo 1499 | local theCreateDef, theChar, inQuotedSeq, theSeqQuote 1500 | local theLastMatchedParenCharNo 1501 | 1502 | put 0 into rStartCharNo 1503 | put 0 into rEndCharNo 1504 | 1505 | ## find actual starting char 1506 | repeat with i = xRootSearchCharNo to the number of chars of pSQL 1507 | if word 1 to -1 of (char i of pSQL) is not empty then 1508 | put i into rStartCharNo 1509 | put i into xRootSearchCharNo 1510 | exit repeat 1511 | end if 1512 | end repeat 1513 | 1514 | repeat with i = xRootSearchCharNo to the number of chars of pSQL 1515 | put char i of pSQL into theChar 1516 | 1517 | if not inQuotedSeq then 1518 | if theChar is "," or theChar is ")" then 1519 | ## all done 1520 | put i into xRootSearchCharNo 1521 | subtract 1 from i 1522 | exit repeat 1523 | else 1524 | -- put theChar after theCreateDef 1525 | put theChar is among the items of quote & ",',`,(" into inQuotedSeq 1526 | if inQuotedSeq then 1527 | put theChar into theSeqQuote 1528 | end if 1529 | end if 1530 | else 1531 | -- put theChar after theCreateDef 1532 | 1533 | ## in quoted sequence 1534 | if theSeqQuote is "(" and theChar is ")" then 1535 | put false into inQuotedSeq 1536 | put i into theLastMatchedParenCharNo 1537 | else if theChar is theSeqQuote then 1538 | if char (i+1) of pSQL is theSeqQuote then 1539 | add 1 to i 1540 | put char i of pSQL after theCreateDef 1541 | else 1542 | put false into inQuotedSeq 1543 | end if 1544 | end if 1545 | end if 1546 | end repeat 1547 | 1548 | if rStartCharNo > 0 then 1549 | repeat with i = i down to 1 1550 | if char i of pSQL is not empty then 1551 | put i into rEndCharNo 1552 | exit repeat 1553 | end if 1554 | end repeat 1555 | 1556 | put char rStartCharNo to rEndCharNo of pSQL into theCreateDef 1557 | end if 1558 | 1559 | return theCreateDef 1560 | end _findNextCreateDef 1561 | 1562 | 1563 | private command _sqlite_dbAlterTable pTable, pTableA, pConnectionA 1564 | local theSQL 1565 | local theConnID 1566 | local theField 1567 | local theFieldsThatLiveAnotherDay 1568 | local theNewCreateSQL, theNewFields 1569 | local theOldFields, theOldCreateSQL 1570 | local theBindingsA 1571 | local theTempName 1572 | 1573 | put dbconn_get("connection id", pConnectionA) into theConnID 1574 | 1575 | put format("SELECT sql FROM sqlite_master WHERE name = '%s'", pTable) into theSQL 1576 | put dbconn_retrieveQueryAsData(theSQL, theBindingsA, empty, empty, pConnectionA) into theOldCreateSQL 1577 | 1578 | ## Get current and new field names 1579 | put revDatabaseColumnNames(theConnID, pTable) into theOldFields 1580 | put dbschema_tableGet(pTable, "fields", pConnectionA["database"]) into theNewFields 1581 | 1582 | ## Determine which fields will exist after the update. These are fields we can insert into. 1583 | set the wholeMatches to true 1584 | repeat for each line theField in theNewFields 1585 | if theField is among the items of theOldFields then 1586 | put theField & comma after theFieldsThatLiveAnotherDay 1587 | end if 1588 | end repeat 1589 | delete the last char of theFieldsThatLiveAnotherDay 1590 | 1591 | ## Extra data from table we are altering 1592 | put "_tmp_" & pTable & "_tmp_" into theTempName 1593 | put "CREATE TEMPORARY table" && quote & theTempName & quote into word 1 to 3 of theOldCreateSQL 1594 | put _sqlite_dbCreateTableSQL(pTable, pTableA, pConnectionA) into theNewCreateSQL 1595 | 1596 | put theOldCreateSQL & ";" & cr into theSQL 1597 | put format("INSERT INTO %s (rowid,%s) SELECT rowid,%s FROM \"%s\";\n", \ 1598 | theTempName, theOldFields, theOldFields, pTable) after theSQL 1599 | put "DROP TABLE" && quote & pTable & quote & ";" & cr after theSQL 1600 | put theNewCreateSQL & ";" & cr after theSQL 1601 | put format("INSERT INTO \"%s\" (rowid,%s) select rowid,%s from \"%s\";\n", \ 1602 | pTable, theFieldsThatLiveAnotherDay, theFieldsThatLiveAnotherDay, theTempName) after theSQL 1603 | put "DROP TABLE" && quote & theTempName & quote & ";" after theSQL 1604 | 1605 | dbconn_executeSQL theSQL, pConnectionA 1606 | 1607 | ## INDEXES 1608 | -- _sqlite_dbCreateTableIndexes pConnectionA, pTableA 1609 | 1610 | return empty 1611 | end _sqlite_dbAlterTable 1612 | 1613 | 1614 | private command _sqlite_dbCreateTableIndexes pName, pIndexA, pConnectionA 1615 | local theQsA, theSQL 1616 | 1617 | put dbconn_get("quotes", pConnectionA) into theQsA 1618 | 1619 | if pIndexA["type"] is "unique" then 1620 | put "CREATE UNIQUE INDEX " into theSQL 1621 | else 1622 | put "CREATE INDEX " into theSQL 1623 | end if 1624 | 1625 | -- NAME on "TABLE" 1626 | put theQsA["left"] & pName & theQsA["right"] && "ON" && theQsA["left"] & pIndexA["table"] & theQsA["right"] after theSQL 1627 | 1628 | put " (" after theSQL 1629 | repeat with i = 1 to the number of elements of pIndexA["fields"] 1630 | if pIndexA["fields"][i]["sort order"] is "descending" then put "DESC" into pIndexA["fields"][i]["sort order"] 1631 | else put "ASC" into pIndexA["fields"][i]["sort order"] 1632 | 1633 | put theQsA["left"] & pIndexA["fields"][i]["name"] & theQsA["right"] && pIndexA["fields"][i]["sort order"] & ", " after theSQL 1634 | end repeat 1635 | put ")" into char -2 to -1 of theSQL 1636 | 1637 | dbconn_executeSQL theSQL, pConnectionA 1638 | 1639 | return empty 1640 | end _sqlite_dbCreateTableIndexes 1641 | 1642 | 1643 | private command _sqlite_dbDeleteIndex pIndexName, pTableName, pConnectionA 1644 | local theQsA, theSQL 1645 | 1646 | put dbconn_get("quotes", pConnectionA) into theQsA 1647 | 1648 | put "DROP INDEX " into theSQL 1649 | put theQsA["left"] & pIndexName & theQsA["right"] after theSQL 1650 | 1651 | dbconn_executeSQL theSQL, pConnectionA 1652 | 1653 | return empty 1654 | end _sqlite_dbDeleteIndex 1655 | 1656 | 1657 | private function _sqlite_systemTables 1658 | return "sqlite_sequence" 1659 | end _sqlite_systemTables 1660 | 1661 | 1662 | ## Creates comparison between each matching item in pLeftFields and pRightFields. 1663 | ## Uses OLD for sqlite trigger 1664 | private function _sqlite_TriggerWhereConditionFromMultipleFields pLeftFields, pRightFields, pTriggerKeyWord 1665 | local i,theSQL 1666 | 1667 | if pTriggerKeyWord is empty then put "OLD" into pTriggerKeyWord 1668 | 1669 | repeat with i = 1 to the number of items of pLeftFields 1670 | put quote & item i of pLeftFields & quote && "= " & pTriggerKeyWord & "." & quote & item i of pRightFields & quote && "AND " after theSQL 1671 | end repeat 1672 | delete the last word of theSQL 1673 | 1674 | ## example: "left_1" = OLD."right_1" AND "left_2" = OLD."right_2" 1675 | return theSQL 1676 | end _sqlite_TriggerWhereConditionFromMultipleFields 1677 | 1678 | 1679 | private command _sqlite_dbCreateForeignKeyConstraints pConnectionA 1680 | local theSQL, theQsA, theTablesA, theTableName, theDeleteStatements 1681 | local theRelationsA, theR, theType, theUpdateAction, theDeleteAction 1682 | local theLeftTable, theRightTable, theCrossRefTable 1683 | local theTableKeyFields, theCrossRefKeyFields, theWhereCondition 1684 | local theRightKeyFields, theLeftKeyFields 1685 | 1686 | ######### 1687 | ## STEP 1: CREATE delete_ TRIGGERS FOR many-to-many, one-to-one and one-to-many 1688 | ## TO DO: one-to-one 1689 | put dbconn_get("quotes", pConnectionA) into theQsA 1690 | put tableobjects_get("tables", pConnectionA["database"]) into theTablesA 1691 | 1692 | repeat with i = 1 to the number of elements of theTablesA 1693 | ## Skip aliases 1694 | if tableobj_get(theTablesA[i], "type") is "alias" then next repeat 1695 | put tableobj_get(theTablesA[i], "name") into theTableName 1696 | put empty into theDeleteStatements 1697 | 1698 | put tableobj_get(theTablesA[i], "relationship indexes") into theRelationsA 1699 | 1700 | repeat with j = 1 to the number of elements of theRelationsA 1701 | put theRelationsA[j] into theR 1702 | 1703 | put tblrelation_get(theR, "type") into theType 1704 | put tblrelation_get(theR, "update action") into theUpdateAction 1705 | put tblrelation_get(theR, "delete action") into theDeleteAction 1706 | put tblrelation_get(theR, "left table") into theLeftTable 1707 | put tblrelation_get(theR, "right table") into theRightTable 1708 | -- put tblrelation_get(theR, "owner") into theOwner -- left, none or right (one-to-one) 1709 | put tblrelation_get(theR, "cross-reference table") into theCrossRefTable 1710 | 1711 | ## Todo: Figure out what to do with update actions 1712 | 1713 | switch theType 1714 | case "many-to-many" 1715 | switch theDeleteAction 1716 | case "restrict" 1717 | ## Todo: Don't allow deletion of left/right until cross-ref has been deleted. 1718 | break 1719 | case "cascade" 1720 | ## Todo: (Verify?) Delete record records from opposite table 1721 | if theTableName is theLeftTable then 1722 | put tblrelation_get(theR, "left table key") into theTableKeyFields 1723 | put tblrelation_get(theR, "cross-reference table key for left table") into theCrossRefKeyFields 1724 | put _sqlite_TriggerWhereConditionFromMultipleFields(theCrossRefKeyFields, theTableKeyFields, "OLD") into theWhereCondition 1725 | put format(" DELETE FROM %s WHERE %s;\n", theCrossRefTable, theWhereCondition ) after theDeleteStatements 1726 | 1727 | else if theTableName is theRightTable then 1728 | put tblrelation_get(theR, "right table key") into theTableKeyFields 1729 | put tblrelation_get(theR, "cross-reference table key for right table") into theCrossRefKeyFields 1730 | put _sqlite_TriggerWhereConditionFromMultipleFields(theCrossRefKeyFields, theTableKeyFields, "OLD") into theWhereCondition 1731 | put format(" DELETE FROM %s WHERE %s;\n", theCrossRefTable, theWhereCondition) after theDeleteStatements 1732 | end if 1733 | break 1734 | case "set null" 1735 | ## todo: Set link in cross-ref table to NULL 1736 | break 1737 | end switch 1738 | break 1739 | 1740 | case "one-to-many" 1741 | switch theDeleteAction 1742 | case "restrict" 1743 | ## todo: Must delete right table records before deleting left table record 1744 | break 1745 | case "cascade" 1746 | ## Delete right table records when deleting left table record 1747 | if theTableName is theLeftTable then 1748 | put tblrelation_get(theR, "right table key") into theRightKeyFields 1749 | put tblrelation_get(theR, "left table key") into theLeftKeyFields 1750 | put _sqlite_TriggerWhereConditionFromMultipleFields(theRightKeyFields, theLeftKeyFields, "OLD") into theWhereCondition 1751 | put format(" DELETE FROM %s WHERE %s;\n", theRightTable, theWhereCondition ) after theDeleteStatements 1752 | end if 1753 | break 1754 | case "set null" 1755 | ## Set right table key to NULL when deleting left table record 1756 | if theTableName is theLeftTable then 1757 | put tblrelation_get(theR, "right table key") into theRightKeyFields 1758 | put tblrelation_get(theR, "left table key") into theLeftKeyFields 1759 | put _sqlite_TriggerWhereConditionFromMultipleFields(theRightKeyFields, theLeftKeyFields, "OLD") into theWhereCondition 1760 | put format(" UPDATE %s SET %s = NULL WHERE %s;\n", \ 1761 | theRightTable, theRightKeyFields, theWhereCondition ) after theDeleteStatements 1762 | end if 1763 | break 1764 | end switch 1765 | break 1766 | 1767 | case "one-to-one" 1768 | switch theDeleteAction 1769 | case "restrict" 1770 | ## todo: Don't allow owner to be deleted until ownee is deleted 1771 | break 1772 | case "cascade" 1773 | ## todo: When the owner is deleted then delete ownee 1774 | -- if theTableName is theTable1Name then 1775 | -- put format(" DELETE FROM %s WHERE %s = OLD.%s;\n", \ 1776 | -- theTable2Name, relationobject_getProp(theObject, "right table key"), \ 1777 | -- relationobject_getProp(theObject, "left table key") ) after theDeleteStatements 1778 | -- end if 1779 | break 1780 | case "set null" 1781 | ## todo: Set ownee to NULL when owner is deleted 1782 | break 1783 | end switch 1784 | break 1785 | end switch 1786 | 1787 | end repeat ## relationships 1788 | 1789 | if theDeleteStatements is not empty then 1790 | put tableobj_get(theTablesA[i], "name") into theTableName 1791 | 1792 | ## DELETE ANY EXISTING TRIGGER 1793 | put "DROP TRIGGER IF EXISTS delete_" & theTableName into theSQL 1794 | dbconn_executeSQL theSQL, pConnectionA 1795 | 1796 | ## CREATE TRIGGER 1797 | put format("CREATE TRIGGER delete_%s AFTER DELETE ON %s\n", theTableName, theQsA["left"] & theTableName & theQsA["right"]) into theSQL 1798 | put "FOR EACH ROW BEGIN" & cr after theSQL 1799 | put theDeleteStatements after theSQL 1800 | put "END" after theSQL 1801 | 1802 | dbconn_executeSQL theSQL, pConnectionA 1803 | end if 1804 | ## END STEP 1 1805 | ############## 1806 | 1807 | end repeat ## tables 1808 | 1809 | return empty 1810 | end _sqlite_dbCreateForeignKeyConstraints 1811 | 1812 | 1813 | --> Driver: MySQL 1814 | 1815 | 1816 | private command _mysql_dbTableFields pConnectionA, pTable 1817 | local theArrayA, theConnectionID, theCursor, theField 1818 | local thePrimaryKeys, theSQL, theQs, theA 1819 | 1820 | put dbconn_get("connection id", pConnectionA) into theConnectionID 1821 | put dbconn_get("quotes", pConnectionA) into theQs 1822 | 1823 | put "SHOW FULL COLUMNS FROM" && theQs["left"] & pTable & theQs["right"] into theSQL 1824 | put revQueryDatabase(theConnectionID, theSQL) into theCursor 1825 | if theCursor is not an integer then 1826 | sqlyoga__throwError sqlyoga__errorTypeFromError(pConnectionA, theCursor), "retrieving mysql table fields" 1827 | end if 1828 | 1829 | if revQueryIsAtEnd(theCursor) then 1830 | revCloseCursor theCursor 1831 | sqlyoga__throwError sqlyoga__errorTypeFromError(pConnectionA, "no fields"), \ 1832 | "no fields were returned for table '" & pTable & "'" 1833 | end if 1834 | 1835 | repeat forever 1836 | put revDatabaseColumnNamed(theCursor, "Field") into theField 1837 | 1838 | put theField & comma after theA["field order"] 1839 | 1840 | -- ODBC cuts off last char for Null and Key. 1841 | -- Taking into account for testing 1842 | put theField into theA["fields"][theField]["name"] 1843 | put revDatabaseColumnNamed(theCursor, "Default") into theA["fields"][theField]["default value"] 1844 | put revDatabaseColumnNamed(theCursor, "Collation") into theA["fields"][theField]["encoding"] 1845 | if theA["fields"][theField]["encoding"] begins with "utf8" then put "utf8" into theA["fields"][theField]["encoding"] 1846 | 1847 | if theA["fields"][theField]["default value"] is empty then 1848 | put "NULL" into theA["fields"][theField]["default value"] 1849 | else if theA["fields"][theField]["default value"] is not "NULL" \ 1850 | and theA["fields"][theField]["default value"] is not a number \ 1851 | and theA["fields"][theField]["default value"] is not "CURRENT_TIMESTAMP" then 1852 | ## add quotes so we can pass directly to the database 1853 | put "'" & theA["fields"][theField]["default value"] & "'" into theA["fields"][theField]["default value"] 1854 | end if 1855 | put revDatabaseColumnNamed(theCursor, "NULL") begins with "YE" into theA["fields"][theField]["accepts null"] 1856 | put revDatabaseColumnNamed(theCursor, "Key") begins with "PR" into theA["fields"][theField]["primary key"] 1857 | put revDatabaseColumnNamed(theCursor, "Key") begins with "UN" into theA["fields"][theField]["unique"] 1858 | if theA["fields"][theField]["primary key"] then 1859 | put theField & comma after thePrimaryKeys 1860 | end if 1861 | 1862 | if revDatabaseColumnNamed(theCursor, "Extra") is "auto_increment" then 1863 | put "sequence" into theA["fields"][theField]["type"] 1864 | put empty into theA["fields"][theField]["length"] 1865 | put false into theA["fields"][theField]["signed"] 1866 | else 1867 | local theType, theLength, isSigned 1868 | 1869 | dbsynch_splitFieldType revDatabaseColumnNamed(theCursor, "Type"), theType, theLength, isSigned 1870 | put theType into theA["fields"][theField]["type"] 1871 | put theLength into theA["fields"][theField]["length"] 1872 | put isSigned into theA["fields"][theField]["signed"] 1873 | end if 1874 | 1875 | revMoveToNextRecord theCursor 1876 | if the result is not true then exit REPEAT 1877 | end REPEAT 1878 | 1879 | delete the last char of thePrimaryKeys 1880 | put thePrimaryKeys into theA["primary key"] 1881 | delete the last char of theA["field order"] 1882 | 1883 | revCloseCursor theCursor 1884 | 1885 | return theA for value 1886 | end _mysql_dbTableFields 1887 | 1888 | 1889 | private command _mysql_dbTableRelationships pConnectionA, pTable 1890 | local theConnectionID 1891 | local theQs 1892 | local theA 1893 | 1894 | put dbconn_get("connection id", pConnectionA) into theConnectionID 1895 | put dbconn_get("quotes", pConnectionA) into theQs 1896 | 1897 | -- create array of relationships the table has with other tables 1898 | -- put "PRAGMA foreign_key_list(" & leftQ & pTable & rightQ & ")" into theSQL 1899 | -- put revQueryDatabase(theConnectionID, theSQL) into theCursor 1900 | -- if theCursor is not an integer then 1901 | 1902 | -- end if 1903 | 1904 | -- if not revQueryIsAtEnd(theCursor) then 1905 | -- repeat forever 1906 | -- add 1 to theIndex 1907 | 1908 | -- put revDatabaseColumnNamed(theCursor, "table") into theReferencedTable 1909 | -- put revDatabaseColumnNamed(theCursor, "from") into theSourceColumns 1910 | -- put revDatabaseColumnNamed(theCursor, "to") into theReferencedColumns 1911 | 1912 | -- put theSourceColumns into theA[theIndex]["from key"] 1913 | -- put theReferencedTable into theA[theIndex]["to table"] 1914 | -- put theReferencedColumns into theA[theIndex]["to key"] 1915 | 1916 | -- revMoveToNextRecord theCursor 1917 | -- if the result is not true then exit repeat 1918 | -- end repeat 1919 | -- end if 1920 | -- revCloseCursor theCursor 1921 | 1922 | return theA for value 1923 | end _mysql_dbTableRelationships 1924 | 1925 | 1926 | private command _mysql_dbTableIndexes pConnectionA, pTable, pFieldsA 1927 | local theArrayA, theCollation, theConnectionID 1928 | local theCursor, theIndexName, theSQL, theQs, theA 1929 | 1930 | put dbconn_get("connection id", pConnectionA) into theConnectionID 1931 | put dbconn_get("quotes", pConnectionA) into theQs 1932 | 1933 | -- http://dev.mysql.com/doc/refman/5.0/en/show-index.html 1934 | put "SHOW INDEX FROM" && pTable into theSQL 1935 | put revQueryDatabase( theConnectionID, theSQL) into theCursor 1936 | if theCursor is not an integer then 1937 | sqlyoga__throwError sqlyoga__errorTypeFromError(pConnectionA, theCursor), "retrieving mysql index list" 1938 | end if 1939 | 1940 | if not revdb_iseof(theCursor) then 1941 | repeat forever 1942 | put revDatabaseColumnNamed(theCursor, "Key_name") into theIndexName 1943 | put revDatabaseColumnNamed(theCursor, "Non_unique") is 0 into theA["indexes"][theIndexName]["unique"] 1944 | put revDatabaseColumnNamed(theCursor, "Column_name") into \ 1945 | line (the number of lines of theA["indexes"][theIndexName]["fields"] + 1) of theA["indexes"][theIndexName]["fields"] 1946 | put revDatabaseColumnNamed(theCursor, "Collation") into theCollation 1947 | put revDatabaseColumnNamed(theCursor, "Index_type") into theA["indexes"][theIndexName]["type"] 1948 | 1949 | ## TODO: ADD CHECK FOR FULL TEXT 1950 | if theA["indexes"][theIndexName]["unique"] then 1951 | put "unique" into theA["indexes"][theIndexName]["style"] 1952 | else 1953 | put "simple" into theA["indexes"][theIndexName]["style"] 1954 | end if 1955 | 1956 | put theIndexName is "PRIMARY" into theA["indexes"][theIndexName]["primary key"] 1957 | if theCollation is "A" then put "ascending" into theA["indexes"][theIndexName]["sort direction"] 1958 | else put "descending" into theA["indexes"][theIndexName]["sort direction"] 1959 | 1960 | revMoveToNextRecord theCursor 1961 | if the result is not true then exit REPEAT 1962 | end repeat 1963 | end if 1964 | 1965 | revCloseCursor theCursor 1966 | 1967 | return theA for value 1968 | end _mysql_dbTableIndexes 1969 | 1970 | 1971 | private function _mysql_getFieldSQLType pFieldA 1972 | local theType, theLimit, thePrecision, theScale, theSQLType 1973 | 1974 | -- boolean, decimal, float, double precision, integer, timestamp, date, date time, binary, sequence, list, text 1975 | put _effectiveFieldtype(pFieldA) into theType 1976 | put pFieldA["length"] into theLimit 1977 | put pFieldA["precision"] into thePrecision 1978 | put pFieldA["scale"] into theScale 1979 | 1980 | put theType into theSQLType 1981 | 1982 | switch theType 1983 | case "text" 1984 | if pFieldA["fixed width"] then 1985 | put "CHAR" into theSQLType 1986 | if theLimit is not an integer then 1987 | put 255 into theLimit 1988 | end if 1989 | end if 1990 | 1991 | if theLimit is an integer then 1992 | if theLimit <= 1024 then 1993 | if theSQLType is "text" then 1994 | put "VARCHAR" into theSQLType 1995 | end if 1996 | end if 1997 | 1998 | put theSQLType & "(" & theLimit & ")" into theSQLType 1999 | end if 2000 | break 2001 | 2002 | case "float" 2003 | put "FLOAT" into theSQLType 2004 | break 2005 | 2006 | case "double precision" 2007 | put "DOUBLE PRECISION" into theSQLType 2008 | break 2009 | 2010 | case "decimal" 2011 | if thePrecision is not an integer then 2012 | put "10" into thePrecision 2013 | end if 2014 | if theScale is not an integer then 2015 | put "2" into theScale 2016 | end if 2017 | put theSQLType & "(" & thePrecision & comma & theScale & ")" into theSQLType 2018 | break 2019 | 2020 | case "integer" 2021 | if theLimit is not empty then 2022 | put " (" & theLimit & ")" after theSQLType 2023 | end if 2024 | if not pFieldA["signed"] then 2025 | put " UNSIGNED" after theSQLType 2026 | end if 2027 | break 2028 | 2029 | case "list" 2030 | put "ENUM" into theSQLType 2031 | break 2032 | 2033 | case "date time" 2034 | put "DATETIME" into theSQLType 2035 | break 2036 | 2037 | case "binary" 2038 | put "blob" into theSQLType 2039 | break 2040 | end switch 2041 | 2042 | return theSQLType 2043 | end _mysql_getFieldSQLType 2044 | 2045 | 2046 | private function _mysql_systemTables 2047 | return empty 2048 | end _mysql_systemTables 2049 | 2050 | 2051 | private command _mysql_dbCreateTable pName, pTableA, pConnectionA 2052 | local theQsA, theSQL 2053 | 2054 | put dbconn_get("quotes", pConnectionA) into theQsA 2055 | 2056 | put "CREATE TABLE" && theQsA["left"] & pName & theQsA["right"] into theSQL 2057 | put false into pTableA["@primary key is taken care of"] 2058 | 2059 | ## Fields 2060 | put " (" after theSQL 2061 | repeat with i = 1 to the number of elements of pTableA["fields"] 2062 | put _mysql_getColumnDefSQL(pTableA, pTableA["fields"][i]) after theSQL 2063 | put comma & space after theSQL 2064 | end repeat 2065 | 2066 | ## Primary key 2067 | if pTableA["primary key"] is not empty and not pTableA["@primary key is taken care of"] then 2068 | put " PRIMARY KEY(" & pTableA["primary key"] & "), " after theSQL 2069 | end if 2070 | 2071 | ## Foreign keys 2072 | if the keys of pTableA["foreign keys"] is not empty then 2073 | put _mysql_ForeignKeySQL(pTableA["foreign keys"], pConnectionA) after theSQL 2074 | put ")" after theSQL 2075 | else 2076 | put ")" into char -2 to -1 of theSQL 2077 | end if 2078 | 2079 | ## Database type 2080 | if pTableA["vendor"]["mysql"]["type"] is not empty then 2081 | put " ENGINE = " & pTableA["vendor"]["mysql"]["type"] after theSQL 2082 | else if the keys of pTableA["foreign keys"] is not empty then 2083 | ## NOTE: InnoDB DOES NOT SUPPORT FULLTEXT INDEXES. WE MAY NEED TO TAKE THIS INTO ACCOUNT 2084 | ## IN THE FUTURE 2085 | put " ENGINE = InnoDB" after theSQL 2086 | else 2087 | put " ENGINE = MyISAM" after theSQL 2088 | end if 2089 | 2090 | dbconn_executeSQL theSQL, pConnectionA 2091 | 2092 | return empty 2093 | end _mysql_dbCreateTable 2094 | 2095 | 2096 | -- pTableA["@primary key is taken care of"] can be modified 2097 | function _mysql_getColumnDefSQL @pTableA, pFieldA 2098 | local theQsA, theSQLType, theSQL 2099 | 2100 | put "`" into theQsA["left"] 2101 | put "`" into theQsA["right"] 2102 | put _mysql_getFieldSQLType(pFieldA) into theSQLType 2103 | 2104 | ## Name 2105 | put theQsA["left"] & pFieldA["name"] & theQsA["right"] & space into theSQL 2106 | 2107 | if pFieldA["type"] is "sequence" \ 2108 | and (pTableA["primary key"] is pFieldA["name"] or pTableA["primary key"] is empty) then 2109 | put "INT(11) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY " after theSQL 2110 | put pFieldA["name"] into pTableA["primary key"] 2111 | put true into pTableA["@primary key is taken care of"] 2112 | else 2113 | put theSQLType & space after theSQL 2114 | 2115 | if "default" is among the keys of pFieldA then 2116 | switch pFieldA["type"] 2117 | case "boolean" 2118 | if pFieldA["default"] is among the items of "0,false" then put "0" into pFieldA["default"] 2119 | else if pFieldA["default"] is among the items of "1,true" then put "1" into pFieldA["default"] 2120 | break 2121 | end switch 2122 | put "DEFAULT " & pFieldA["default"] & " " after theSQL 2123 | end if 2124 | 2125 | if pFieldA["accepts null"] is false then 2126 | put "NOT NULL " after theSQL 2127 | end if 2128 | end if 2129 | delete the last char of theSQL 2130 | 2131 | return theSQL 2132 | end _mysql_getColumnDefSQL 2133 | 2134 | 2135 | private function _mysql_ForeignKeySQL pForeignKeysA, pConnectionA 2136 | local theSQL, theQsA 2137 | 2138 | put dbconn_get("quotes", pConnectionA) into theQsA 2139 | 2140 | -- sqlyoga_addToArray theTableA["foreign keys"], "key:step_id", "references:steps", "on fields:id", "on delete:cascade", "on update:cascade" 2141 | repeat with i = 1 to the number of elements of pForeignKeysA 2142 | if pForeignKeysA[i]["name"] is not empty then 2143 | put "CONSTRAINT " & pForeignKeysA[i]["name"] & " " after theSQL 2144 | end if 2145 | 2146 | put "FOREIGN KEY (" & pForeignKeysA[i]["key"] & ") " & \ 2147 | "REFERENCES " & pForeignKeysA[i]["references"] & "(" & pForeignKeysA[i]["on fields"] & ")" after theSQL 2148 | -- if pForeignKeysA[i]["name"] is not empty then put " " after pForeignKeysA[i]["name"] 2149 | 2150 | -- put "FOREIGN KEY " & pForeignKeysA[i]["name"] & "(" & pForeignKeysA[i]["key"] & ") " & \ 2151 | -- "REFERENCES " & pForeignKeysA[i]["references"] & "(" & pForeignKeysA[i]["on fields"] & ")" after theSQL 2152 | 2153 | repeat for each item theItem in "on delete,on update" 2154 | if pForeignKeysA[i][theItem] is not empty then 2155 | put " " & toUpper(theItem) && pForeignKeysA[i][theItem] after theSQL 2156 | end if 2157 | end repeat 2158 | 2159 | put ", " after theSQL 2160 | end repeat 2161 | delete char -2 to -1 of theSQL 2162 | 2163 | return theSQL 2164 | end _mysql_ForeignKeySQL 2165 | 2166 | 2167 | private command _mysql_afterMigration pConnectionA 2168 | return empty 2169 | end _mysql_afterMigration 2170 | 2171 | 2172 | private command _mysql_dbCreateFields pTable, pFieldsA, pConnectionA 2173 | local theQsA, theTableA 2174 | local theBaseSQL, theSQL 2175 | 2176 | put dbconn_get("quotes", pConnectionA) into theQsA 2177 | 2178 | put empty into theTableA["primary key"] 2179 | put true into theTableA["@primary key is taken care of"] 2180 | 2181 | put "ALTER TABLE " & theQsA["left"] & pTable & theQsA["right"] & " " into theBaseSQL 2182 | 2183 | repeat with i = 1 to the number of elements of pFieldsA 2184 | put theBaseSQL & "ADD COLUMN " & _mysql_getColumnDefSQL(theTableA, pFieldsA[i]) into theSQL 2185 | dbconn_executeSQL theSQL, pConnectionA 2186 | end repeat 2187 | 2188 | return empty 2189 | end _mysql_dbCreateFields 2190 | 2191 | 2192 | private command _mysql_dbDeleteFields pTable, pFields, pConnectionA 2193 | local theQsA, theBaseSQL, theSQL 2194 | 2195 | put dbconn_get("quotes", pConnectionA) into theQsA 2196 | 2197 | put "ALTER TABLE " & theQsA["left"] & pTable & theQsA["right"] & " " into theBaseSQL 2198 | 2199 | ## Mysql supports multiple drop statements in a single query 2200 | ## could change later if desired. 2201 | repeat for each item theField in pFields 2202 | put theBaseSQL & "DROP COLUMN " & theQsA["left"] & theField & theQsA["right"] into theSQL 2203 | dbconn_executeSQL theSQL, pConnectionA 2204 | end repeat 2205 | 2206 | return empty 2207 | end _mysql_dbDeleteFields 2208 | 2209 | 2210 | private command _mysql_dbAlterTable pConnectionA, pTableA 2211 | 2212 | end _mysql_dbAlterTable 2213 | 2214 | 2215 | private command _mysql_dbCreateTableIndexes pName, pIndexA, pConnectionA 2216 | local theQsA, theSQL, theLimit 2217 | 2218 | put dbconn_get("quotes", pConnectionA) into theQsA 2219 | 2220 | switch pIndexA["type"] 2221 | case "unique" 2222 | put "CREATE UNIQUE INDEX " into theSQL 2223 | break 2224 | case "fulltext" 2225 | put "CREATE FULLTEXT INDEX " into theSQL 2226 | break 2227 | case "spatial" 2228 | put "CREATE SPATIAL INDEX " into theSQL 2229 | break 2230 | default 2231 | put "CREATE INDEX " into theSQL 2232 | end switch 2233 | 2234 | -- NAME on "TABLE" 2235 | put theQsA["left"] & pName & theQsA["right"] && "ON" && theQsA["left"] & pIndexA["table"] & theQsA["right"] after theSQL 2236 | 2237 | put " (" after theSQL 2238 | repeat with i = 1 to the number of elements of pIndexA["fields"] 2239 | if pIndexA["fields"][i]["sort order"] is "descending" then put "DESC" into pIndexA["fields"][i]["sort order"] 2240 | else put "ASC" into pIndexA["fields"][i]["sort order"] 2241 | 2242 | ## INDEXING TEXT OR BLOG COLUMNS REQUIRE SOME SORT OF INDEX VALUE. 2243 | ## WE USE 20 IF THE SCHEMA DOESN'T SPECIFY ONE (RANDOM NUMBER I CHOSE) 2244 | if pIndexA["fields"][i]["length"] > 0 then 2245 | put " (" & pIndexA["fields"][i]["length"] & ")" into theLimit 2246 | else if pIndexA["fields"][i]["type"] is among the items of "text,binary" then 2247 | put " (" & 20 & ")" into theLimit 2248 | end if 2249 | 2250 | ## TODO: COLLATION SUPPORT. WE DON'T HAVE METATYPES FOR COLLATIONS THOUGH 2251 | put theQsA["left"] & pIndexA["fields"][i]["name"] & theQsA["right"] & theLimit \ 2252 | && pIndexA["fields"][i]["sort order"] & ", " after theSQL 2253 | end repeat 2254 | put ")" into char -2 to -1 of theSQL 2255 | 2256 | dbconn_executeSQL theSQL, pConnectionA 2257 | 2258 | return empty 2259 | end _mysql_dbCreateTableIndexes 2260 | 2261 | 2262 | private command _mysql_dbDeleteIndex pIndexName, pTableName, pConnectionA 2263 | local theQsA, theSQL 2264 | 2265 | put dbconn_get("quotes", pConnectionA) into theQsA 2266 | 2267 | put "DROP INDEX " into theSQL 2268 | put theQsA["left"] & pIndexName & theQsA["right"] && "ON" && theQsA["left"] & pTableName & theQsA["right"] after theSQL 2269 | 2270 | dbconn_executeSQL theSQL, pConnectionA 2271 | 2272 | return empty 2273 | end _mysql_dbDeleteIndex 2274 | 2275 | 2276 | private command _mysql_dbCreateForeignKeyConstraints pConnectionA 2277 | 2278 | end _mysql_dbCreateForeignKeyConstraints 2279 | 2280 | 2281 | --> Driver: PostGreSQL 2282 | 2283 | 2284 | private function _postgresql_getFieldSQLType pFieldA 2285 | local theType, theLimit, thePrecision, theScale, theSQLType 2286 | 2287 | put _effectiveFieldtype(pFieldA) into theType 2288 | put pFieldA["length"] into theLimit 2289 | put pFieldA["precision"] into thePrecision 2290 | put pFieldA["scale"] into theScale 2291 | 2292 | put theType into theSQLType 2293 | 2294 | switch theType 2295 | case "text" 2296 | if pFieldA["fixed width"] then 2297 | put "character" into theSQLType 2298 | if theLimit is not an integer then 2299 | put 255 into theLimit 2300 | end if 2301 | else if theLimit > 0 then 2302 | put "character varying" into theSQLType 2303 | end if 2304 | 2305 | if theLimit > 0 then 2306 | put "(" & theLimit & ")" after theSQLType 2307 | end if 2308 | break 2309 | 2310 | case "float" 2311 | put "real" into theSQLType 2312 | break 2313 | 2314 | case "double precision" 2315 | put "double precision" into theSQLType 2316 | break 2317 | 2318 | case "decimal" 2319 | if thePrecision is not an integer then 2320 | put "10" into thePrecision 2321 | end if 2322 | if theScale is not an integer then 2323 | put "2" into theScale 2324 | end if 2325 | put theSQLType & "(" & thePrecision & comma & theScale & ")" into theSQLType 2326 | break 2327 | 2328 | case "list" 2329 | put "ENUM" into theSQLType 2330 | break 2331 | 2332 | case "date time" 2333 | put "timestamp" into theSQLType 2334 | break 2335 | 2336 | case "binary" 2337 | put "bytea" into theSQLType 2338 | break 2339 | end switch 2340 | 2341 | return theSQLType 2342 | end _postgresql_getFieldSQLType 2343 | 2344 | 2345 | private function _postgresql_systemTables 2346 | return empty 2347 | end _postgresql_systemTables 2348 | 2349 | 2350 | private command _postgresql_dbCreateTable pName, pTableA, pConnectionA 2351 | local theQsA, theSQL 2352 | 2353 | put dbconn_get("quotes", pConnectionA) into theQsA 2354 | 2355 | put "CREATE TABLE" && theQsA["left"] & pName & theQsA["right"] into theSQL 2356 | put false into pTableA["@primary key is taken care of"] 2357 | 2358 | ## Fields 2359 | put " (" after theSQL 2360 | repeat with i = 1 to the number of elements of pTableA["fields"] 2361 | put _postgresql_getColumnDefSQL(pTableA, pTableA["fields"][i]) after theSQL 2362 | put comma & space after theSQL 2363 | end repeat 2364 | 2365 | ## Primary key 2366 | if pTableA["primary key"] is not empty and not pTableA["@primary key is taken care of"] then 2367 | put " PRIMARY KEY(" & pTableA["primary key"] & "), " after theSQL 2368 | end if 2369 | 2370 | ## Foreign keys 2371 | if the keys of pTableA["foreign keys"] is not empty then 2372 | put _postgresql_ForeignKeySQL(pTableA["foreign keys"], pConnectionA) after theSQL 2373 | put ")" after theSQL 2374 | else 2375 | put ")" into char -2 to -1 of theSQL 2376 | end if 2377 | 2378 | dbconn_executeSQL theSQL, pConnectionA 2379 | 2380 | return empty 2381 | end _postgresql_dbCreateTable 2382 | 2383 | 2384 | -- pTableA["@primary key is taken care of"] can be modified 2385 | function _postgresql_getColumnDefSQL @pTableA, pFieldA 2386 | local theQsA, theSQL, theSQLType 2387 | 2388 | put quote into theQsA["left"] 2389 | put quote into theQsA["right"] 2390 | put _postgresql_getFieldSQLType(pFieldA) into theSQLType 2391 | 2392 | ## Name 2393 | put theQsA["left"] & pFieldA["name"] & theQsA["right"] & space into theSQL 2394 | 2395 | if pFieldA["type"] is "sequence" \ 2396 | and (pTableA["primary key"] is pFieldA["name"] or pTableA["primary key"] is empty) then 2397 | put "SERIAL PRIMARY KEY " after theSQL 2398 | put pFieldA["name"] into pTableA["primary key"] 2399 | put true into pTableA["@primary key is taken care of"] 2400 | else 2401 | put theSQLType & space after theSQL 2402 | 2403 | if "default" is among the keys of pFieldA then 2404 | switch pFieldA["type"] 2405 | case "boolean" 2406 | if pFieldA["default"] is among the items of "0,false" then put "FALSE" into pFieldA["default"] 2407 | else if pFieldA["default"] is among the items of "1,true" then put "TRUE" into pFieldA["default"] 2408 | break 2409 | end switch 2410 | put "DEFAULT " & pFieldA["default"] & " " after theSQL 2411 | end if 2412 | 2413 | if pFieldA["accepts null"] is false then 2414 | put "NOT NULL " after theSQL 2415 | end if 2416 | end if 2417 | delete the last char of theSQL 2418 | 2419 | return theSQL 2420 | end _postgresql_getColumnDefSQL 2421 | 2422 | 2423 | private function _postgresql_ForeignKeySQL pForeignKeysA, pConnectionA 2424 | local theSQL, theQsA 2425 | 2426 | put dbconn_get("quotes", pConnectionA) into theQsA 2427 | 2428 | -- sqlyoga_addToArray theTableA["foreign keys"], "key:step_id", "references:steps", "on fields:id", "on delete:cascade", "on update:cascade" 2429 | repeat with i = 1 to the number of elements of pForeignKeysA 2430 | if pForeignKeysA[i]["name"] is not empty then 2431 | put "CONSTRAINT " & pForeignKeysA[i]["name"] & " " after theSQL 2432 | end if 2433 | 2434 | put "FOREIGN KEY (" & pForeignKeysA[i]["key"] & ") " & \ 2435 | "REFERENCES " & pForeignKeysA[i]["references"] & "(" & pForeignKeysA[i]["on fields"] & ")" after theSQL 2436 | 2437 | repeat for each item theItem in "on delete,on update" 2438 | if pForeignKeysA[i][theItem] is not empty then 2439 | put " " & toUpper(theItem) && pForeignKeysA[i][theItem] after theSQL 2440 | end if 2441 | end repeat 2442 | 2443 | put ", " after theSQL 2444 | end repeat 2445 | delete char -2 to -1 of theSQL 2446 | 2447 | return theSQL 2448 | end _postgresql_ForeignKeySQL 2449 | 2450 | 2451 | private command _postgresql_afterMigration pConnectionA 2452 | return empty 2453 | end _postgresql_afterMigration 2454 | 2455 | 2456 | private command _postgresql_dbCreateFields pTable, pFieldsA, pConnectionA 2457 | local theQsA, theTableA, theBaseSQL, theSQL 2458 | 2459 | put dbconn_get("quotes", pConnectionA) into theQsA 2460 | 2461 | put empty into theTableA["primary key"] 2462 | put true into theTableA["@primary key is taken care of"] 2463 | 2464 | put "ALTER TABLE " & theQsA["left"] & pTable & theQsA["right"] & " " into theBaseSQL 2465 | 2466 | repeat with i = 1 to the number of elements of pFieldsA 2467 | put theBaseSQL & "ADD COLUMN " & _postgresql_getColumnDefSQL(theTableA, pFieldsA[i]) into theSQL 2468 | dbconn_executeSQL theSQL, pConnectionA 2469 | end repeat 2470 | 2471 | return empty 2472 | end _postgresql_dbCreateFields 2473 | 2474 | 2475 | private command _postgresql_dbDeleteFields pTable, pFields, pConnectionA 2476 | local theQsA, theBaseSQL, theSQL 2477 | 2478 | put dbconn_get("quotes", pConnectionA) into theQsA 2479 | 2480 | put "ALTER TABLE " & theQsA["left"] & pTable & theQsA["right"] & " " into theBaseSQL 2481 | 2482 | repeat for each item theField in pFields 2483 | put theBaseSQL & "DROP COLUMN " & theQsA["left"] & theField & theQsA["right"] into theSQL 2484 | dbconn_executeSQL theSQL, pConnectionA 2485 | end repeat 2486 | 2487 | return empty 2488 | end _postgresql_dbDeleteFields 2489 | 2490 | 2491 | private command _postgresql_dbAlterTable pConnectionA, pTableA 2492 | 2493 | end _postgresql_dbAlterTable 2494 | 2495 | 2496 | private command _postgresql_dbCreateTableIndexes pName, pIndexA, pConnectionA 2497 | local theQsA, theSQL, theLimit 2498 | 2499 | put dbconn_get("quotes", pConnectionA) into theQsA 2500 | 2501 | switch pIndexA["type"] 2502 | case "unique" 2503 | put "CREATE UNIQUE INDEX " into theSQL 2504 | break 2505 | default 2506 | put "CREATE INDEX " into theSQL 2507 | end switch 2508 | 2509 | -- NAME on "TABLE" 2510 | put theQsA["left"] & pName & theQsA["right"] && "ON" && theQsA["left"] & pIndexA["table"] & theQsA["right"] after theSQL 2511 | 2512 | put " (" after theSQL 2513 | repeat with i = 1 to the number of elements of pIndexA["fields"] 2514 | if pIndexA["fields"][i]["sort order"] is "descending" then put "DESC" into pIndexA["fields"][i]["sort order"] 2515 | else put "ASC" into pIndexA["fields"][i]["sort order"] 2516 | 2517 | if pIndexA["fields"][i]["length"] > 0 then 2518 | put " (" & pIndexA["fields"][i]["length"] & ")" into theLimit 2519 | end if 2520 | 2521 | ## TODO: COLLATION SUPPORT. WE DON'T HAVE METATYPES FOR COLLATIONS THOUGH 2522 | put theQsA["left"] & pIndexA["fields"][i]["name"] & theQsA["right"] & theLimit \ 2523 | && pIndexA["fields"][i]["sort order"] & ", " after theSQL 2524 | end repeat 2525 | put ")" into char -2 to -1 of theSQL 2526 | 2527 | dbconn_executeSQL theSQL, pConnectionA 2528 | 2529 | return empty 2530 | end _postgresql_dbCreateTableIndexes 2531 | 2532 | 2533 | private command _postgresql_dbDeleteIndex pIndexName, pTableName, pConnectionA 2534 | local theQsA, theSQL 2535 | 2536 | put dbconn_get("quotes", pConnectionA) into theQsA 2537 | 2538 | put "DROP INDEX " into theSQL 2539 | put theQsA["left"] & pIndexName & theQsA["right"] after theSQL 2540 | 2541 | dbconn_executeSQL theSQL, pConnectionA 2542 | 2543 | return empty 2544 | end _postgresql_dbDeleteIndex 2545 | 2546 | 2547 | private command _postgresql_dbCreateForeignKeyConstraints pConnectionA 2548 | 2549 | end _postgresql_dbCreateForeignKeyConstraints 2550 | 2551 | 2552 | private function _postgresql_ForeignKeySQL pForeignKeysA, pConnectionA 2553 | local theSQL, theQsA 2554 | 2555 | put dbconn_get("quotes", pConnectionA) into theQsA 2556 | 2557 | -- sqlyoga_addToArray theTableA["foreign keys"], "key:step_id", "references:steps", "on fields:id", "on delete:cascade", "on update:cascade" 2558 | repeat with i = 1 to the number of elements of pForeignKeysA 2559 | put "FOREIGN KEY (" & pForeignKeysA[i]["key"] & ") " & \ 2560 | "REFERENCES " & pForeignKeysA[i]["references"] & "(" & pForeignKeysA[i]["on fields"] & ")" after theSQL 2561 | 2562 | repeat for each item theItem in "on delete,on update" 2563 | if pForeignKeysA[i][theItem] is not empty then 2564 | put " " & toUpper(theItem) && pForeignKeysA[i][theItem] after theSQL 2565 | end if 2566 | end repeat 2567 | 2568 | put ", " after theSQL 2569 | end repeat 2570 | delete char -2 to -1 of theSQL 2571 | 2572 | return theSQL 2573 | end _postgresql_ForeignKeySQL 2574 | 2575 | 2576 | private command _postgresql_dbTableFields pConnectionA, pTable 2577 | local theArrayA, theConnectionID, theCursor, theField 2578 | local thePrimaryKeys, theQuotes, theSQL, theEncoding 2579 | local theQs 2580 | 2581 | set the wholeMatches to true 2582 | 2583 | put dbconn_get("connection id", pConnectionA) into theConnectionID 2584 | put dbconn_get("quotes", pConnectionA) into theQs 2585 | put false into pConnectionA["import schema"] ## no recursion, thank you 2586 | 2587 | ## primary key 2588 | ## PRIMARY KEY 2589 | put "SELECT c.COLUMN_NAME FROM information_schema.table_constraints pk, " & \ 2590 | "information_schema.key_column_usage c " & \ 2591 | "WHERE pk.table_name = '" & pTable & "' " & \ 2592 | "AND CONSTRAINT_TYPE = 'PRIMARY KEY' " & \ 2593 | "AND c.TABLE_NAME = pk.TABLE_NAME " & \ 2594 | "AND c.CONSTRAINT_NAME = pk.CONSTRAINT_NAME" into theSQL 2595 | put false into pConnectionA["import schema"] ## no recursion, thank you 2596 | put dbconn_retrieveQueryAsData(theSQL, empty, empty, comma, pConnectionA) into thePrimaryKeys 2597 | 2598 | ## Get DB encoding to assign to fields 2599 | put "SHOW SERVER_ENCODING" into theSQL 2600 | put dbconn_retrieveQueryAsData(theSQL, empty, empty, comma, pConnectionA) into theEncoding 2601 | 2602 | put tolower(theEncoding) into theEncoding 2603 | 2604 | put "SELECT * FROM information_schema.columns " & \ 2605 | "WHERE table_schema != 'information_schema' AND table_name = '" & pTable & "' " & \ 2606 | "ORDER BY ordinal_position" into theSQL 2607 | put revQueryDatabase(theConnectionID, theSQL) into theCursor 2608 | if theCursor is not an integer then 2609 | sqlyoga__throwError sqlyoga__errorTypeFromError(pConnectionA, theCursor), "retrieving postgresql table fields" 2610 | end if 2611 | 2612 | if revQueryIsAtEnd(theCursor) then 2613 | revCloseCursor theCursor 2614 | sqlyoga__throwError sqlyoga__errorTypeFromError(pConnectionA, "no fields"), \ 2615 | "no fields were returned for table '" & pTable & "'" 2616 | end if 2617 | 2618 | local theA, theChar 2619 | 2620 | repeat forever 2621 | put revDatabaseColumnNamed(theCursor, "column_name") into theField 2622 | 2623 | put theField & comma after theA["field order"] 2624 | 2625 | put theField into theA["fields"][theField]["name"] 2626 | put theEncoding into theA["fields"][theField]["encoding"] 2627 | 2628 | put revDatabaseColumnNamed(theCursor, "column_default") into theA["fields"][theField]["default value"] 2629 | if theA["fields"][theField]["default value"] is empty then put "NULL" into theA["fields"][theField]["default value"] 2630 | put revDatabaseColumnNamed(theCursor, "is_nullable") is "YES" into theA["fields"][theField]["accepts null"] 2631 | put theField is among the items of thePrimaryKeys into theA["fields"][theField]["primary key"] 2632 | 2633 | ## Cleanse ::character... 2634 | repeat with i = the number of chars of theA["fields"][theField]["default value"] down to 1 2635 | put char i of theA["fields"][theField]["default value"] into theChar 2636 | if theChar is "'" then exit repeat 2637 | if theChar is ":" and (char i-1 of theA["fields"][theField]["default value"] is ":") then 2638 | delete char i-1 to -1 of theA["fields"][theField]["default value"] 2639 | exit repeat 2640 | end if 2641 | end repeat 2642 | -- put revDatabaseColumnNamed(theCursor, "Key") is "UNI" into theA["fields"][theField]["unique"] 2643 | 2644 | put revDatabaseColumnNamed(theCursor, "data_type") into theA["fields"][theField]["type"] 2645 | 2646 | if theA["fields"][theField]["default value"] begins with "nextval(" \ 2647 | and theA["fields"][theField]["type"] is "integer" then 2648 | put "sequence" into theA["fields"][theField]["type"] 2649 | put empty into theA["fields"][theField]["length"] 2650 | put false into theA["fields"][theField]["signed"] 2651 | put empty into theA["fields"][theField]["default value"] 2652 | else 2653 | put revDatabaseColumnNamed(theCursor, "numeric_precision") into theA["fields"][theField]["length"] 2654 | if theA["fields"][theField]["length"] is empty then 2655 | put revDatabaseColumnNamed(theCursor, "character_maximum_length") into theA["fields"][theField]["length"] 2656 | end if 2657 | 2658 | ## How to find this out? 2659 | put false into theA["fields"][theField]["signed"] 2660 | end if 2661 | 2662 | revMoveToNextRecord theCursor 2663 | if the result is not true then exit REPEAT 2664 | end REPEAT 2665 | 2666 | put thePrimaryKeys into theA["primary key"] 2667 | delete the last char of theA["field order"] 2668 | 2669 | revCloseCursor theCursor 2670 | 2671 | return theA for value 2672 | end _postgresql_dbTableFields 2673 | 2674 | 2675 | private command _postgresql_dbTableIndexes pConnectionA, pTable, @pFieldsA 2676 | local theArrayA, theCollation, theConnectionID, theQs 2677 | local theCursor, theIndexName, theSQL 2678 | 2679 | put dbconn_get("connection id", pConnectionA) into theConnectionID 2680 | put dbconn_get("quotes", pConnectionA) into theQs 2681 | 2682 | -- http://dev.mysql.com/doc/refman/5.0/en/show-index.html 2683 | put "SHOW INDEX FROM" && pTable into theSQL 2684 | put revQueryDatabase( theConnectionID, theSQL) into theCursor 2685 | if theCursor is not an integer then 2686 | sqlyoga__throwError sqlyoga__errorTypeFromError(pConnectionA, theCursor), "retrieving mysql index list" 2687 | end if 2688 | 2689 | local theA 2690 | 2691 | if not revdb_iseof(theCursor) then 2692 | repeat forever 2693 | put revDatabaseColumnNamed(theCursor, "Key_name") into theIndexName 2694 | put revDatabaseColumnNamed(theCursor, "Non_unique") is 0 into theA["indexes"][theIndexName]["unique"] 2695 | put revDatabaseColumnNamed(theCursor, "Column_name") into \ 2696 | line (the number of lines of theA["indexes"][theIndexName]["fields"] + 1) of theA["indexes"][theIndexName]["fields"] 2697 | put revDatabaseColumnNamed(theCursor, "Collation") into theCollation 2698 | put revDatabaseColumnNamed(theCursor, "Index_type") into theA["indexes"][theIndexName]["type"] 2699 | 2700 | ## TODO: ADD CHECK FOR FULL TEXT 2701 | if theA["indexes"][theIndexName]["unique"] then 2702 | put "unique" into theA["indexes"][theIndexName]["style"] 2703 | else 2704 | put "simple" into theA["indexes"][theIndexName]["style"] 2705 | end if 2706 | 2707 | put theIndexName is "PRIMARY" into theA["indexes"][theIndexName]["primary key"] 2708 | if theCollation is "A" then put "ascending" into theA["indexes"][theIndexName]["sort direction"] 2709 | else put "descending" into theA["indexes"][theIndexName]["sort direction"] 2710 | 2711 | revMoveToNextRecord theCursor 2712 | if the result is not true then exit REPEAT 2713 | end repeat 2714 | end if 2715 | 2716 | revCloseCursor theCursor 2717 | 2718 | return theA for value 2719 | end _postgresql_dbTableIndexes 2720 | 2721 | 2722 | private function _postgresql_systemTables 2723 | get "sql_features,sql_implementation_info,sql_languages,sql_packages,sql_parts,sql_sizing,sql_sizing_profiles" 2724 | replace comma with cr in it 2725 | return it 2726 | end _postgresql_systemTables 2727 | 2728 | 2729 | --> Driver: Valentina 2730 | 2731 | 2732 | private function _valentina_getFieldSQLType pFieldA 2733 | local theType, theLimit, thePrecision, theScale, theSQLType 2734 | 2735 | put _effectiveFieldtype(pFieldA) into theType 2736 | put pFieldA["length"] into theLimit 2737 | put pFieldA["precision"] into thePrecision 2738 | put pFieldA["scale"] into theScale 2739 | 2740 | put theType into theSQLType 2741 | 2742 | switch theType 2743 | case "text" 2744 | if pFieldA["fixed width"] then 2745 | put "CHAR" into theSQLType 2746 | if theLimit is not an integer then 2747 | put 255 into theLimit 2748 | end if 2749 | end if 2750 | 2751 | if theLimit is an integer then 2752 | if theLimit <= 255 then 2753 | if theSQLType is "text" then 2754 | put "VARCHAR" into theSQLType 2755 | end if 2756 | end if 2757 | 2758 | put theSQLType & "(" & theLimit & ")" into theSQLType 2759 | end if 2760 | break 2761 | 2762 | case "float" 2763 | put "FLOAT" into theSQLType 2764 | break 2765 | 2766 | case "double precision" 2767 | put "DOUBLE PRECISION" into theSQLType 2768 | break 2769 | 2770 | case "decimal" 2771 | if thePrecision is not an integer then 2772 | put "10" into thePrecision 2773 | end if 2774 | if theScale is not an integer then 2775 | put "2" into theScale 2776 | end if 2777 | put theSQLType & "(" & thePrecision & comma & theScale & ")" into theSQLType 2778 | break 2779 | 2780 | case "list" 2781 | put "ENUM" into theSQLType 2782 | break 2783 | end switch 2784 | 2785 | return theSQLType 2786 | end _valentina_getFieldSQLType 2787 | 2788 | 2789 | private command _valentina_dbTableFields pConnectionA, pTable 2790 | local theQs, theA, theConnectionID, theCursor, theField 2791 | local thePrimaryKeys, theQuotes, theSQL, theRowsA 2792 | 2793 | put dbconn_get("connection id", pConnectionA) into theConnectionID 2794 | put dbconn_get("quotes", pConnectionA) into theQs 2795 | 2796 | put "SHOW COLUMNS FROM " & theQs["left"] & pTable & theQs["right"] into theSQL 2797 | put false into pConnectionA["import schema"] ## no recursion, thank you 2798 | dbconn_retrieveQueryAsArray theSQL, empty, empty, theRowsA, pConnectionA 2799 | 2800 | if the keys of theRowsA is empty then 2801 | sqlyoga__throwError sqlyoga__errorTypeFromError(pConnectionA, "no fields"), \ 2802 | "no fields were returned for table '" & pTable & "'" 2803 | end if 2804 | 2805 | ## Every table has a rec id 2806 | put "RecID" into theA["fields"]["RecID"]["name"] 2807 | put "sequence" into theA["fields"]["RecID"]["type"] 2808 | put NULL into theA["fields"]["RecID"]["default value"] 2809 | put false into theA["fields"]["RecID"]["accepts null"] 2810 | put empty into theA["fields"]["RecID"]["length"] 2811 | put false into theA["fields"]["RecID"]["signed"] 2812 | 2813 | repeat with i = 1 to the number of elements of theRowsA 2814 | put theRowsA[i]["fld_name"] into theField 2815 | put theField & comma after theA["field order"] 2816 | put theField into theA["fields"][theField]["name"] 2817 | put empty into theA["fields"][theField]["encoding"] 2818 | 2819 | put theRowsA[i]["fld_default_value"] into theA["fields"][theField]["default value"] 2820 | if theA["fields"][theField]["default value"] is empty \ 2821 | or theA["fields"][theField]["default value"] is null then 2822 | put "NULL" into theA["fields"][theField]["default value"] 2823 | else if theA["fields"][theField]["default value"] is not an integer then 2824 | put "'" & theA["fields"][theField]["default value"] & "'" into theA["fields"][theField]["default value"] 2825 | end if 2826 | put theRowsA[i]["fld_nullable"] is true into theA["fields"][theField]["accepts null"] 2827 | put theRowsA[i]["fld_identity"] is true into theA["fields"][theField]["primary key"] 2828 | put theRowsA[i]["fld_type"] into theA["fields"][theField]["type"] 2829 | 2830 | put theRowsA[i]["fld_max_length"] into theA["fields"][theField]["length"] 2831 | if theA["fields"][theField]["length"] is 0 then put empty into theA["fields"][theField]["length"] 2832 | 2833 | ## How to find this out? 2834 | put false into theA["fields"][theField]["signed"] 2835 | 2836 | if theA["fields"][theField]["primary key"] then 2837 | put theField & comma after thePrimaryKeys 2838 | end if 2839 | 2840 | ## Assign primary key a sequence 2841 | if theA["fields"][theField]["primary key"] then 2842 | put "sequence" into theA["fields"][theField]["type"] 2843 | put empty into theA["fields"][theField]["length"] 2844 | put false into theA["fields"][theField]["signed"] 2845 | end if 2846 | end repeat 2847 | 2848 | delete the last char of thePrimaryKeys 2849 | put thePrimaryKeys into theA["primary key"] 2850 | delete the last char of theA["field order"] 2851 | 2852 | ## Default to Valentina special key 2853 | if theA["primary key"] is empty then 2854 | put "RecID" into theA["primary key"] 2855 | put "RecID" into item (the number of items of theA["field order"] + 1) of theA["field order"] 2856 | end if 2857 | 2858 | return theA for value 2859 | end _valentina_dbTableFields 2860 | 2861 | 2862 | private command _valentina_dbTableIndexes pConnectionA, pTable, pFieldsA 2863 | local theA 2864 | 2865 | -- put dbconn_get("connection id", pConnectionA) into theConnectionID 2866 | -- put dbconn_get("quotes", pConnectionA) into theQs 2867 | 2868 | -- --> http://dev.mysql.com/doc/refman/5.0/en/show-index.html 2869 | -- put "SHOW INDEX FROM" && pTable into theSQL 2870 | -- put revQueryDatabase( theConnectionID, theSQL) into theCursor 2871 | -- if theCursor is not an integer then 2872 | 2873 | -- end if 2874 | 2875 | -- if not revdb_iseof(theCursor) then 2876 | -- repeat forever 2877 | -- put revDatabaseColumnNamed(theCursor, "Key_name") into theIndexName 2878 | -- put revDatabaseColumnNamed(theCursor, "Non_unique") is 0 into theA["indexes"][theIndexName]["unique"] 2879 | -- put revDatabaseColumnNamed(theCursor, "Column_name") into \ 2880 | -- line (the number of lines of theA["indexes"][theIndexName]["fields"] + 1) of theA["indexes"][theIndexName]["fields"] 2881 | -- put revDatabaseColumnNamed(theCursor, "Collation") into theCollation 2882 | -- put revDatabaseColumnNamed(theCursor, "Index_type") into theA["indexes"][theIndexName]["type"] 2883 | 2884 | -- ## TODO: ADD CHECK FOR FULL TEXT 2885 | -- if theA["indexes"][theIndexName]["unique"] then 2886 | -- put "unique" into theA["indexes"][theIndexName]["style"] 2887 | -- else 2888 | -- put "simple" into theA["indexes"][theIndexName]["style"] 2889 | -- end if 2890 | 2891 | -- put theIndexName is "PRIMARY" into theA["indexes"][theIndexName]["primary key"] 2892 | -- if theCollation is "A" then put "ascending" into theA["indexes"][theIndexName]["sort direction"] 2893 | -- else put "descending" into theA["indexes"][theIndexName]["sort direction"] 2894 | 2895 | -- revMoveToNextRecord theCursor 2896 | -- if the result is not true then exit REPEAT 2897 | -- end repeat 2898 | -- end if 2899 | 2900 | -- revCloseCursor theCursor 2901 | 2902 | return theA for value 2903 | end _valentina_dbTableIndexes 2904 | 2905 | 2906 | private function _valentina_systemTables 2907 | return empty 2908 | end _valentina_systemTables 2909 | 2910 | 2911 | --> Driver: Filemaker 2912 | 2913 | 2914 | private function _filemaker_getFieldSQLType pFieldA 2915 | 2916 | end _filemaker_getFieldSQLType 2917 | 2918 | 2919 | private command _filemaker_dbTableFields pConnectionA, pTable 2920 | local theConnectionID, theQs, theSQL, theRowsA 2921 | local thePrimaryKeys, theField, theA 2922 | 2923 | set the wholeMatches to true 2924 | 2925 | put dbconn_get("connection id", pConnectionA) into theConnectionID 2926 | put dbconn_get("quotes", pConnectionA) into theQs 2927 | put false into pConnectionA["import schema"] ## no recursion, thank you 2928 | 2929 | -- SQLTables - catalog information is stored and reported as single part names (table name only). 2930 | -- - SQLColumns 2931 | -- - SQLColumnPrivileges 2932 | -- - SQLDescribeCol 2933 | -- - SQLGetTypeInfo 2934 | 2935 | put "SELECT SQLTables" into theSQL 2936 | dbconn_retrieveQueryAsArray theSQL, empty, empty, theRowsA, pConnectionA 2937 | 2938 | if the keys of theRowsA is empty then 2939 | sqlyoga__throwError sqlyoga__errorTypeFromError(pConnectionA, "no fields"), \ 2940 | "no fields were returned for table '" & pTable & "'" 2941 | end if 2942 | 2943 | ## PRIMARY KEY 2944 | put "SELECT c.COLUMN_NAME FROM information_schema.table_constraints pk, " & \ 2945 | "information_schema.key_column_usage c " & \ 2946 | "WHERE pk.table_name = '" & pTable & "' " & \ 2947 | "AND CONSTRAINT_TYPE = 'PRIMARY KEY' " & \ 2948 | "AND c.TABLE_NAME = pk.TABLE_NAME " & \ 2949 | "AND c.CONSTRAINT_NAME = pk.CONSTRAINT_NAME" into theSQL 2950 | put false into pConnectionA["import schema"] ## no recursion, thank you 2951 | put dbconn_retrieveQueryAsData(theSQL, empty, empty, comma, pConnectionA) into thePrimaryKeys 2952 | 2953 | repeat with i = 1 to the number of elements of theRowsA 2954 | put theRowsA[i]["COLUMN_NAME"] into theField 2955 | put theField & comma after theA["field order"] 2956 | put theField into theA["fields"][theField]["name"] 2957 | 2958 | put theField is among the items of thePrimaryKeys into theA["fields"][theField]["primary key"] 2959 | put theRowsA[i]["COLUMN_DEF"] into theA["fields"][theField]["default value"] 2960 | if theA["fields"][theField]["default value"] is empty then 2961 | put "NULL" into theA["fields"][theField]["default value"] 2962 | end if 2963 | put theRowsA[i]["IS_NULLABLE"] is "YES" into theA["fields"][theField]["accepts null"] 2964 | put theRowsA[i]["TYPE_NAME"] into theA["fields"][theField]["type"] 2965 | put theRowsA[i]["PRECISION"] into theA["fields"][theField]["length"] 2966 | 2967 | if the last word of theA["fields"][theField]["type"] is "identity" then 2968 | put "sequence" into theA["fields"][theField]["type"] 2969 | put false into theA["fields"][theField]["signed"] 2970 | else 2971 | ## How to find this out? 2972 | put false into theA["fields"][theField]["signed"] 2973 | end if 2974 | end REPEAT 2975 | 2976 | put thePrimaryKeys into theA["primary key"] 2977 | delete the last char of theA["field order"] 2978 | 2979 | return theA for value 2980 | end _filemaker_dbTableFields 2981 | 2982 | 2983 | private command _filemaker_dbTableIndexes pConnectionA, pTable, pFieldsA 2984 | local theA 2985 | 2986 | -- put dbconn_get("connection id", pConnectionA) into theConnectionID 2987 | -- put dbconn_get("quotes", pConnectionA) into theQs 2988 | 2989 | -- --> http://dev.mysql.com/doc/refman/5.0/en/show-index.html 2990 | -- put "SHOW INDEX FROM" && pTable into theSQL 2991 | -- put revQueryDatabase( theConnectionID, theSQL) into theCursor 2992 | -- if theCursor is not an integer then 2993 | 2994 | -- end if 2995 | 2996 | -- if not revdb_iseof(theCursor) then 2997 | -- repeat forever 2998 | -- put revDatabaseColumnNamed(theCursor, "Key_name") into theIndexName 2999 | -- put revDatabaseColumnNamed(theCursor, "Non_unique") is 0 into theA["indexes"][theIndexName]["unique"] 3000 | -- put revDatabaseColumnNamed(theCursor, "Column_name") into \ 3001 | -- line (the number of lines of theA["indexes"][theIndexName]["fields"] + 1) of theA["indexes"][theIndexName]["fields"] 3002 | -- put revDatabaseColumnNamed(theCursor, "Collation") into theCollation 3003 | -- put revDatabaseColumnNamed(theCursor, "Index_type") into theA["indexes"][theIndexName]["type"] 3004 | 3005 | -- ## TODO: ADD CHECK FOR FULL TEXT 3006 | -- if theA["indexes"][theIndexName]["unique"] then 3007 | -- put "unique" into theA["indexes"][theIndexName]["style"] 3008 | -- else 3009 | -- put "simple" into theA["indexes"][theIndexName]["style"] 3010 | -- end if 3011 | 3012 | -- put theIndexName is "PRIMARY" into theA["indexes"][theIndexName]["primary key"] 3013 | -- if theCollation is "A" then put "ascending" into theA["indexes"][theIndexName]["sort direction"] 3014 | -- else put "descending" into theA["indexes"][theIndexName]["sort direction"] 3015 | 3016 | -- revMoveToNextRecord theCursor 3017 | -- if the result is not true then exit REPEAT 3018 | -- end repeat 3019 | -- end if 3020 | 3021 | -- revCloseCursor theCursor 3022 | 3023 | return theA for value 3024 | end _filemaker_dbTableIndexes 3025 | 3026 | 3027 | private function _filemaker_systemTables 3028 | -- get "sql_features,sql_implementation_info,sql_languages,sql_packages,sql_parts,sql_sizing,sql_sizing_profiles" 3029 | -- replace comma with cr in it 3030 | -- return it 3031 | return empty 3032 | end _filemaker_systemTables 3033 | 3034 | 3035 | --> Driver: SQL Server 3036 | 3037 | 3038 | private function _sqlserver_getFieldSQLType pFieldA, pConnectionA 3039 | local theType, theLimit, thePrecision, theScale, theEncoding, theSQLType 3040 | 3041 | put _effectiveFieldtype(pFieldA) into theType 3042 | put pFieldA["length"] into theLimit 3043 | put pFieldA["precision"] into thePrecision 3044 | put pFieldA["scale"] into theScale 3045 | put pFieldA["encoding"] into theEncoding 3046 | 3047 | put theType into theSQLType 3048 | 3049 | switch theType 3050 | case "text" 3051 | if pFieldA["fixed width"] then 3052 | put "char" into theSQLType 3053 | if theLimit is not an integer then 3054 | put 255 into theLimit 3055 | end if 3056 | end if 3057 | 3058 | if theLimit is an integer then 3059 | if theLimit <= 1024 then 3060 | if theSQLType is "text" then 3061 | put "varchar" into theSQLType 3062 | end if 3063 | end if 3064 | 3065 | put theSQLType & "(" & theLimit & ")" into theSQLType 3066 | end if 3067 | 3068 | if theEncoding begins with "utf" or theEncoding begins with "unicode" then 3069 | put "n" before theSQLType 3070 | end if 3071 | break 3072 | 3073 | case "float" 3074 | put "DOUBLE" into theSQLType 3075 | break 3076 | 3077 | case "double precision" 3078 | put "DOUBLE PRECISION" into theSQLType 3079 | break 3080 | 3081 | case "decimal" 3082 | if thePrecision is not an integer then 3083 | put "10" into thePrecision 3084 | end if 3085 | if theScale is not an integer then 3086 | put "2" into theScale 3087 | end if 3088 | put theSQLType & "(" & thePrecision & comma & theScale & ")" into theSQLType 3089 | break 3090 | 3091 | case "boolean" 3092 | put "BIT" into theSQLType 3093 | break 3094 | 3095 | case "list" 3096 | put "ENUM" into theSQLType 3097 | break 3098 | 3099 | case "date time" 3100 | ## datetime2 is 2008 (10.x.xxxx.xx) and later. 3101 | ## for 2005 (9.xx.xxxx.xx) we need datetime 3102 | set the itemDelimiter to "." 3103 | if item 1 of dbconn_get("version", pConnectionA) >= 10 then 3104 | put "datetime2" into theSQLType 3105 | if thePrecision is not an integer then 3106 | put "7" into thePrecision 3107 | end if 3108 | put "(" & thePrecision & ")" after theSQLType 3109 | else 3110 | put "datetime" into theSQLType 3111 | end if 3112 | break 3113 | 3114 | case "binary" 3115 | if theLimit is not an integer then 3116 | put 8000 into theLimit 3117 | end if 3118 | 3119 | if pFieldA["fixed width"] then 3120 | put "binary" into theSQLType 3121 | put min(8000, theLimit) into theLimit 3122 | else 3123 | put "varbinary" into theSQLType 3124 | if theLimit > 8000 then 3125 | put "max" into theLimit 3126 | end if 3127 | end if 3128 | 3129 | if theLimit is not empty then 3130 | ## theLimit could be MAX 3131 | put theSQLType & "(" & theLimit & ")" into theSQLType 3132 | end if 3133 | break 3134 | end switch 3135 | 3136 | return theSQLType 3137 | end _sqlserver_getFieldSQLType 3138 | 3139 | 3140 | private command _sqlserver_dbTableFields pConnectionA, pTable 3141 | local theConnectionID, theQs, theSQL, theRowsA 3142 | local thePrimaryKeys, theField, theA 3143 | 3144 | set the wholeMatches to true 3145 | 3146 | put dbconn_get("connection id", pConnectionA) into theConnectionID 3147 | put dbconn_get("quotes", pConnectionA) into theQs 3148 | put false into pConnectionA["import schema"] ## no recursion, thank you 3149 | 3150 | -- put "SELECT * FROM information_schema.columns " & \ 3151 | -- "WHERE table_name = " & theQs["left"] & pTable & theQs["right"] & " " & \ 3152 | -- "ORDER BY ORDINAL_POSITION" into theSQL 3153 | put "SP_COLUMNS N'" & pTable & "'" into theSQL 3154 | dbconn_retrieveQueryAsArray theSQL, empty, empty, theRowsA, pConnectionA 3155 | 3156 | if the keys of theRowsA is empty then 3157 | sqlyoga__throwError sqlyoga__errorTypeFromError(pConnectionA, "no fields"), \ 3158 | "no fields were returned for table '" & pTable & "'" 3159 | end if 3160 | 3161 | ## PRIMARY KEY 3162 | put "SELECT C.COLUMN_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS pk, " & \ 3163 | "INFORMATION_SCHEMA.KEY_COLUMN_USAGE c " & \ 3164 | "WHERE pk.TABLE_NAME = '" & pTable & "' " & \ 3165 | "AND CONSTRAINT_TYPE = 'PRIMARY KEY' " & \ 3166 | "AND c.TABLE_NAME = pk.TABLE_NAME " & \ 3167 | "AND c.CONSTRAINT_NAME = pk.CONSTRAINT_NAME" into theSQL 3168 | put false into pConnectionA["import schema"] ## no recursion, thank you 3169 | put dbconn_retrieveQueryAsData(theSQL, empty, empty, comma, pConnectionA) into thePrimaryKeys 3170 | 3171 | repeat with i = 1 to the number of elements of theRowsA 3172 | put theRowsA[i]["COLUMN_NAME"] into theField 3173 | put theField & comma after theA["field order"] 3174 | put theField into theA["fields"][theField]["name"] 3175 | 3176 | if theRowsA[i]["TYPE_NAME"] is among the items of "nvarchar,ntext,nchar" then 3177 | put "ucs2" into theA["fields"][theField]["encoding"] 3178 | else 3179 | put empty into theA["fields"][theField]["encoding"] 3180 | end if 3181 | 3182 | put theField is among the items of thePrimaryKeys into theA["fields"][theField]["primary key"] 3183 | put theRowsA[i]["COLUMN_DEF"] into theA["fields"][theField]["default value"] 3184 | if theA["fields"][theField]["default value"] is empty then 3185 | put "NULL" into theA["fields"][theField]["default value"] 3186 | end if 3187 | put theRowsA[i]["IS_NULLABLE"] is "YES" into theA["fields"][theField]["accepts null"] 3188 | put theRowsA[i]["TYPE_NAME"] into theA["fields"][theField]["type"] 3189 | put theRowsA[i]["PRECISION"] into theA["fields"][theField]["length"] 3190 | 3191 | if the last word of theA["fields"][theField]["type"] is "identity" then 3192 | put "sequence" into theA["fields"][theField]["type"] 3193 | put false into theA["fields"][theField]["signed"] 3194 | else 3195 | ## How to find this out? 3196 | put false into theA["fields"][theField]["signed"] 3197 | end if 3198 | end REPEAT 3199 | 3200 | put thePrimaryKeys into theA["primary key"] 3201 | delete the last char of theA["field order"] 3202 | 3203 | return theA for value 3204 | end _sqlserver_dbTableFields 3205 | 3206 | 3207 | private command _sqlserver_dbTableIndexes pConnectionA, pTable, pFieldsA 3208 | local theA 3209 | 3210 | -- put dbconn_get("connection id", pConnectionA) into theConnectionID 3211 | -- put dbconn_get("quotes", pConnectionA) into theQs 3212 | 3213 | -- --> http://dev.mysql.com/doc/refman/5.0/en/show-index.html 3214 | -- put "SHOW INDEX FROM" && pTable into theSQL 3215 | -- put revQueryDatabase( theConnectionID, theSQL) into theCursor 3216 | -- if theCursor is not an integer then 3217 | 3218 | -- end if 3219 | 3220 | -- if not revdb_iseof(theCursor) then 3221 | -- repeat forever 3222 | -- put revDatabaseColumnNamed(theCursor, "Key_name") into theIndexName 3223 | -- put revDatabaseColumnNamed(theCursor, "Non_unique") is 0 into theA["indexes"][theIndexName]["unique"] 3224 | -- put revDatabaseColumnNamed(theCursor, "Column_name") into \ 3225 | -- line (the number of lines of theA["indexes"][theIndexName]["fields"] + 1) of theA["indexes"][theIndexName]["fields"] 3226 | -- put revDatabaseColumnNamed(theCursor, "Collation") into theCollation 3227 | -- put revDatabaseColumnNamed(theCursor, "Index_type") into theA["indexes"][theIndexName]["type"] 3228 | 3229 | -- ## TODO: ADD CHECK FOR FULL TEXT 3230 | -- if theA["indexes"][theIndexName]["unique"] then 3231 | -- put "unique" into theA["indexes"][theIndexName]["style"] 3232 | -- else 3233 | -- put "simple" into theA["indexes"][theIndexName]["style"] 3234 | -- end if 3235 | 3236 | -- put theIndexName is "PRIMARY" into theA["indexes"][theIndexName]["primary key"] 3237 | -- if theCollation is "A" then put "ascending" into theA["indexes"][theIndexName]["sort direction"] 3238 | -- else put "descending" into theA["indexes"][theIndexName]["sort direction"] 3239 | 3240 | -- revMoveToNextRecord theCursor 3241 | -- if the result is not true then exit REPEAT 3242 | -- end repeat 3243 | -- end if 3244 | 3245 | -- revCloseCursor theCursor 3246 | 3247 | return theA for value 3248 | end _sqlserver_dbTableIndexes 3249 | 3250 | 3251 | private function _sqlserver_systemTables 3252 | -- get "sql_features,sql_implementation_info,sql_languages,sql_packages,sql_parts,sql_sizing,sql_sizing_profiles" 3253 | -- replace comma with cr in it 3254 | -- return it 3255 | return empty 3256 | end _sqlserver_systemTables 3257 | 3258 | 3259 | private command _sqlserver_dbCreateTable pName, pTableA, pConnectionA 3260 | local theQsA, theSQL 3261 | 3262 | put dbconn_get("quotes", pConnectionA) into theQsA 3263 | 3264 | put "CREATE TABLE" && theQsA["left"] & pName & theQsA["right"] into theSQL 3265 | put false into pTableA["@primary key is taken care of"] 3266 | 3267 | ## Fields 3268 | put " (" after theSQL 3269 | repeat with i = 1 to the number of elements of pTableA["fields"] 3270 | put _sqlserver_getColumnDefSQL(pTableA, pTableA["fields"][i], pConnectionA) after theSQL 3271 | put comma & space after theSQL 3272 | end repeat 3273 | 3274 | ## Primary key 3275 | if pTableA["primary key"] is not empty and not pTableA["@primary key is taken care of"] then 3276 | put " PRIMARY KEY(" & pTableA["primary key"] & "), " after theSQL 3277 | end if 3278 | 3279 | ## Foreign keys 3280 | if the keys of pTableA["foreign keys"] is not empty then 3281 | put _sqlserver_ForeignKeySQL(pTableA["foreign keys"], pConnectionA) after theSQL 3282 | put ")" after theSQL 3283 | else 3284 | put ")" into char -2 to -1 of theSQL 3285 | end if 3286 | 3287 | dbconn_executeSQL theSQL, pConnectionA 3288 | 3289 | return empty 3290 | end _sqlserver_dbCreateTable 3291 | 3292 | 3293 | -- pTableA["@primary key is taken care of"] can be modified 3294 | function _sqlserver_getColumnDefSQL @pTableA, pFieldA, pConnectionA 3295 | local theQsA, theSQLType, theSQL 3296 | 3297 | put quote into theQsA["left"] 3298 | put quote into theQsA["right"] 3299 | put _sqlserver_getFieldSQLType(pFieldA, pConnectionA) into theSQLType 3300 | 3301 | ## Name 3302 | put theQsA["left"] & pFieldA["name"] & theQsA["right"] & space into theSQL 3303 | 3304 | if pFieldA["type"] is "sequence" \ 3305 | and (pTableA["primary key"] is pFieldA["name"] or pTableA["primary key"] is empty) then 3306 | put "INTEGER IDENTITY (1,1) PRIMARY KEY CLUSTERED " after theSQL 3307 | put pFieldA["name"] into pTableA["primary key"] 3308 | put true into pTableA["@primary key is taken care of"] 3309 | else 3310 | put theSQLType & space after theSQL 3311 | 3312 | if "default" is among the keys of pFieldA then 3313 | switch pFieldA["type"] 3314 | case "boolean" 3315 | if pFieldA["default"] is among the items of "0,false" then put "0" into pFieldA["default"] 3316 | else if pFieldA["default"] is among the items of "1,true" then put "1" into pFieldA["default"] 3317 | break 3318 | end switch 3319 | put "DEFAULT " & pFieldA["default"] & " " after theSQL 3320 | end if 3321 | 3322 | if pFieldA["accepts null"] is false then 3323 | put "NOT NULL " after theSQL 3324 | end if 3325 | end if 3326 | delete the last char of theSQL 3327 | 3328 | return theSQL 3329 | end _sqlserver_getColumnDefSQL 3330 | 3331 | 3332 | private function _sqlserver_ForeignKeySQL pForeignKeysA, pConnectionA 3333 | local theSQL, theQsA 3334 | 3335 | put dbconn_get("quotes", pConnectionA) into theQsA 3336 | 3337 | -- sqlyoga_addToArray theTableA["foreign keys"], "key:step_id", "references:steps", "on fields:id", "on delete:cascade", "on update:cascade" 3338 | repeat with i = 1 to the number of elements of pForeignKeysA 3339 | if pForeignKeysA[i]["name"] is not empty then 3340 | put "CONSTRAINT " & pForeignKeysA[i]["name"] & " " after theSQL 3341 | end if 3342 | 3343 | put "FOREIGN KEY (" & pForeignKeysA[i]["key"] & ") " & \ 3344 | "REFERENCES " & pForeignKeysA[i]["references"] & "(" & pForeignKeysA[i]["on fields"] & ")" after theSQL 3345 | 3346 | repeat for each item theItem in "on delete,on update" 3347 | if pForeignKeysA[i][theItem] is not empty then 3348 | put " " & toUpper(theItem) && pForeignKeysA[i][theItem] after theSQL 3349 | end if 3350 | end repeat 3351 | 3352 | put ", " after theSQL 3353 | end repeat 3354 | delete char -2 to -1 of theSQL 3355 | 3356 | return theSQL 3357 | end _sqlserver_ForeignKeySQL 3358 | 3359 | 3360 | private command _sqlserver_afterMigration pConnectionA 3361 | return empty 3362 | end _sqlserver_afterMigration 3363 | 3364 | 3365 | private command _sqlserver_dbCreateFields pTable, pFieldsA, pConnectionA 3366 | local theQsA, theTableA, theBaseSQL, theSQL 3367 | 3368 | put dbconn_get("quotes", pConnectionA) into theQsA 3369 | 3370 | put empty into theTableA["primary key"] 3371 | put true into theTableA["@primary key is taken care of"] 3372 | 3373 | put "ALTER TABLE " & theQsA["left"] & pTable & theQsA["right"] & " " into theBaseSQL 3374 | 3375 | repeat with i = 1 to the number of elements of pFieldsA 3376 | put theBaseSQL & "ADD " & _sqlserver_getColumnDefSQL(theTableA, pFieldsA[i]) into theSQL 3377 | dbconn_executeSQL theSQL, pConnectionA 3378 | end repeat 3379 | 3380 | return empty 3381 | end _sqlserver_dbCreateFields 3382 | 3383 | 3384 | private command _sqlserver_dbAlterTable pConnectionA, pTableA 3385 | 3386 | end _sqlserver_dbAlterTable 3387 | 3388 | 3389 | private command _sqlserver_dbCreateTableIndexes pName, pIndexA, pConnectionA 3390 | local theQsA, theSQL, theLimit 3391 | 3392 | put dbconn_get("quotes", pConnectionA) into theQsA 3393 | 3394 | switch pIndexA["type"] 3395 | case "unique" 3396 | put "CREATE UNIQUE INDEX " into theSQL 3397 | break 3398 | default 3399 | put "CREATE INDEX " into theSQL 3400 | end switch 3401 | 3402 | -- NAME on "TABLE" 3403 | put theQsA["left"] & pName & theQsA["right"] && "ON" && theQsA["left"] & pIndexA["table"] & theQsA["right"] after theSQL 3404 | 3405 | put " (" after theSQL 3406 | repeat with i = 1 to the number of elements of pIndexA["fields"] 3407 | if pIndexA["fields"][i]["sort order"] is "descending" then put "DESC" into pIndexA["fields"][i]["sort order"] 3408 | else put "ASC" into pIndexA["fields"][i]["sort order"] 3409 | 3410 | if pIndexA["fields"][i]["length"] > 0 then 3411 | put " (" & pIndexA["fields"][i]["length"] & ")" into theLimit 3412 | end if 3413 | 3414 | ## TODO: COLLATION SUPPORT. WE DON'T HAVE METATYPES FOR COLLATIONS THOUGH 3415 | put theQsA["left"] & pIndexA["fields"][i]["name"] & theQsA["right"] & theLimit \ 3416 | && pIndexA["fields"][i]["sort order"] & ", " after theSQL 3417 | end repeat 3418 | put ")" into char -2 to -1 of theSQL 3419 | 3420 | dbconn_executeSQL theSQL, pConnectionA 3421 | 3422 | return empty 3423 | end _sqlserver_dbCreateTableIndexes 3424 | 3425 | 3426 | private command _sqlserver_dbDeleteIndex pIndexName, pTableName, pConnectionA 3427 | local theQsA, theSQL 3428 | 3429 | put dbconn_get("quotes", pConnectionA) into theQsA 3430 | 3431 | put "DROP INDEX " into theSQL 3432 | put theQsA["left"] & pTableName & theQsA["right"] & "." & theQsA["left"] & pIndexName & theQsA["right"] after theSQL 3433 | 3434 | dbconn_executeSQL theSQL, pConnectionA 3435 | 3436 | return empty 3437 | end _sqlserver_dbDeleteIndex 3438 | 3439 | 3440 | --> Private 3441 | 3442 | 3443 | ## duplicate of function in libSQLYoga 3444 | private function _valentinaErrorSchema pConnID 3445 | local theError, theErrNum 3446 | 3447 | put VDatabase_ErrNumber( pConnID ) into theErrNum 3448 | if theErrNum > 0 then 3449 | put VDatabase_ErrString( pConnID ) into theError 3450 | else if theErrNum < 0 then 3451 | put "OS error ("& theErrNum &")" into theError 3452 | end if 3453 | 3454 | return theError 3455 | end _valentinaErrorSchema 3456 | 3457 | 3458 | private function _NormalizeFieldType pFromType 3459 | local theType 3460 | 3461 | switch pFromType 3462 | case "BIT" 3463 | case "BOOLEAN" 3464 | put "boolean" into theType 3465 | break 3466 | case "REAL" 3467 | case "FLOAT" 3468 | case "DOUBLE" 3469 | put "float" into theType 3470 | case "DOUBLE PRECISION" 3471 | put "double precision" into theType 3472 | break 3473 | case "decimal" 3474 | case "numeric" 3475 | put "decimal" into theType 3476 | break 3477 | case "LONG" 3478 | case "INTEGER" 3479 | case "INT" 3480 | case "SMALLINT" 3481 | case "ULong" 3482 | case "LLong" 3483 | case "ULLong" 3484 | case "Medium" 3485 | case "MEDIUMINT" 3486 | case "UMedium" 3487 | case "short" 3488 | case "ushort" 3489 | case "Byte" 3490 | case "ObjectPtr" 3491 | put "integer" into theType 3492 | break 3493 | case "TIMESTAMP" 3494 | put "timestamp" into theType 3495 | break 3496 | case "DATE" 3497 | put "date" into theType 3498 | break 3499 | case "TIME" 3500 | case "DATETIME" 3501 | case "DATE TIME" 3502 | put "date time" into theType 3503 | break 3504 | case "BLOB" 3505 | case "TINYBLOB" 3506 | case "MEDIUMBLOB" 3507 | case "LONGBLOB" 3508 | case "FixedBinary" 3509 | case "VarBinary" 3510 | case "LongVarBinary" 3511 | case "BINARY" 3512 | case "bytea" ## postgresql 3513 | put "binary" into theType 3514 | break 3515 | case "sequence" 3516 | put "sequence" into theType 3517 | break 3518 | case "ENUM" 3519 | case "list" 3520 | put "list" into theType 3521 | break 3522 | case "TEXT" 3523 | case "CLOB" 3524 | case "WORD" 3525 | case "CHAR" 3526 | case "STRING" 3527 | case "WSTRING" 3528 | case "VarChar" 3529 | case "LongVarChar" 3530 | case "nvarchar" -- sql server ucs-2 3531 | case "nchar" -- sql server ucs-2 3532 | case "ntext" -- sql server ucs-2 3533 | case "character varying" ## postgresql 3534 | case "citext" ## postgresql 3535 | default 3536 | put "text" into theType 3537 | break 3538 | end switch 3539 | 3540 | return theType 3541 | end _NormalizeFieldType 3542 | 3543 | 3544 | private function _effectiveFieldType pFieldA 3545 | if pFieldA["meta type"] is not empty then 3546 | return pFieldA["meta type"] 3547 | else 3548 | return pFieldA["type"] 3549 | end if 3550 | end _effectiveFieldType 3551 | --------------------------------------------------------------------------------