From 28af951e05ba84d8c100b083958f7d05b4239078 Mon Sep 17 00:00:00 2001 From: Michael Kleehammer Date: Sat, 18 Feb 2017 10:30:11 -0600 Subject: [PATCH] Consolidate all documentation in the Wiki. This updates the pyodbc site to point to the Wiki. Fixes #185 --- README.md | 19 +- docs/api-connection.md | 372 ----------------------------------- docs/api-cursor.md | 431 ----------------------------------------- docs/api-errors.md | 25 --- docs/api-module.md | 100 ---------- docs/api-row.md | 60 ------ docs/encodings.py | 12 -- docs/index.md | 243 +---------------------- docs/releases.md | 71 ------- docs/unicode.md | 156 --------------- 10 files changed, 17 insertions(+), 1472 deletions(-) delete mode 100644 docs/api-connection.md delete mode 100644 docs/api-cursor.md delete mode 100644 docs/api-errors.md delete mode 100644 docs/api-module.md delete mode 100644 docs/api-row.md delete mode 100755 docs/encodings.py delete mode 100644 docs/releases.md delete mode 100644 docs/unicode.md diff --git a/README.md b/README.md index 753324a8..dadb4bbe 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,17 @@ - # pyodbc -pyodbc makes it easy to connect to databases using ODBC. It implements the DB -API 2.0 specification, but adds even more convenience. - -The library is designed to be stand-alone. It does not have other dependencies -and only uses Python's built-in data types. +pyodbc is an open source Python module that makes accessing ODBC databases simple. It +implements the [DB API 2.0](https://www.python.org/dev/peps/pep-0249) specification but is +packed with even more Pythonic convenience. -To install, use pip or setuptools:: +The easiest way to install is to use pip: pip install pyodbc + +Precompiled binary wheels are provided for most Python versions on Windows and macOS. On other +operating systems this will build from source. + +[Documentation](https://github.com/mkleehammer/pyodbc/wiki) + +[Release Notes](https://github.com/mkleehammer/pyodbc/releases) -[Documentation](http://mkleehammer.github.io/pyodbc) diff --git a/docs/api-connection.md b/docs/api-connection.md deleted file mode 100644 index faf98448..00000000 --- a/docs/api-connection.md +++ /dev/null @@ -1,372 +0,0 @@ - -# Connection - -Connection objects manage connections to the database. - -Each manages a single ODBC HDBC. - -There is no constructor -- connections can only be created by the connect function. - -## variables - -### autocommit - -True if the connection is in autocommit mode; False otherwise. Changing this value updates the -ODBC autocommit setting. The default is False. - -### searchescape - -The ODBC search pattern escape character, as returned by SQLGetInfo(SQL_SEARCH_PATTERN_ESCAPE), -used to escape special characters such as '%' and '_'. These are driver specific. - -### timeout - -An optional integer query timeout, in seconds. Use zero, the default, to disable. - -The timeout is applied to all cursors created by the connection, so it cannot be changed for a -given connection. - -If a query timeout occurs, the database should raise an OperationalError with SQLSTATE HYT00 or -HYT01. - -Note: This attribute only affects queries. To set the timeout for the actual connection -process, use the `timeout` keyword of the `connect` function. - -## cursor - - cnxn.cursor() --> Cursor - -Returns a new Cursor Object using the connection. - -pyodbc supports multiple cursors per connection but your database or driver may not. Check -your documentation. - -## commit - - cnxn.commit() - -Commits any pending transaction to the database. - -Pending transactions are automatically rolled back when a connection is closed, so be sure to -call this. - -## rollback - - cnxn.rollback() - -Causes the the database to roll back to the start of any pending transaction. - -You can call this even if no work has been performed on the cursor, allowing it to be used in -finally statements, etc. - -## close - - cnxn.close() - -Closes the connection. Connections are automatically closed when they are deleted, but you -should call this if the connection is referenced in more than one place. - -The connection will be unusable from this point forward and a ProgrammingError will be raised -if any operation is attempted with the connection. The same applies to all cursor objects -trying to use the connection. - -Note that closing a connection without committing the changes first will cause an implicit -rollback to be performed. - -## getinfo - - getinfo(type) --> str | int | bool - - cnxn.getinfo(pyodbc.SQL_ACCESSIBLE_PROCEDURES) - -Returns general information about the driver and data source associated with a connection by -calling [SQLGetInfo](http://msdn.microsoft.com/en-us/library/ms711681%28VS.85%29.aspx) and -returning its results. See Microsoft's SQLGetInfo documentation for the types of information -available. - -pyodbc provides constants for the supported types. - -type | result type ----- | ------------ -SQL_ACCESSIBLE_PROCEDURES | bool -SQL_ACCESSIBLE_TABLES | bool -SQL_ACTIVE_ENVIRONMENTS | int -SQL_AGGREGATE_FUNCTIONS | int -SQL_ALTER_DOMAIN | int -SQL_ALTER_TABLE | int -SQL_ASYNC_MODE | int -SQL_BATCH_ROW_COUNT | int -SQL_BATCH_SUPPORT | int -SQL_BOOKMARK_PERSISTENCE | int -SQL_CATALOG_LOCATION | int -SQL_CATALOG_NAME | bool -SQL_CATALOG_NAME_SEPARATOR | str -SQL_CATALOG_TERM | str -SQL_CATALOG_USAGE | int -SQL_COLLATION_SEQ | str -SQL_COLUMN_ALIAS | bool -SQL_CONCAT_NULL_BEHAVIOR | int -SQL_CONVERT_FUNCTIONS | int -SQL_CONVERT_VARCHAR | int -SQL_CORRELATION_NAME | int -SQL_CREATE_ASSERTION | int -SQL_CREATE_CHARACTER_SET | int -SQL_CREATE_COLLATION | int -SQL_CREATE_DOMAIN | int -SQL_CREATE_SCHEMA | int -SQL_CREATE_TABLE | int -SQL_CREATE_TRANSLATION | int -SQL_CREATE_VIEW | int -SQL_CURSOR_COMMIT_BEHAVIOR | int -SQL_CURSOR_ROLLBACK_BEHAVIOR | int -SQL_DATABASE_NAME | str -SQL_DATA_SOURCE_NAME | str -SQL_DATA_SOURCE_READ_ONLY | bool -SQL_DATETIME_LITERALS | int -SQL_DBMS_NAME | str -SQL_DBMS_VER | str -SQL_DDL_INDEX | int -SQL_DEFAULT_TXN_ISOLATION | int -SQL_DESCRIBE_PARAMETER | bool -SQL_DM_VER | str -SQL_DRIVER_NAME | str -SQL_DRIVER_ODBC_VER | str -SQL_DRIVER_VER | str -SQL_DROP_ASSERTION | int -SQL_DROP_CHARACTER_SET | int -SQL_DROP_COLLATION | int -SQL_DROP_DOMAIN | int -SQL_DROP_SCHEMA | int -SQL_DROP_TABLE | int -SQL_DROP_TRANSLATION | int -SQL_DROP_VIEW | int -SQL_DYNAMIC_CURSOR_ATTRIBUTES1 | int -SQL_DYNAMIC_CURSOR_ATTRIBUTES2 | int -SQL_EXPRESSIONS_IN_ORDERBY | bool -SQL_FILE_USAGE | int -SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1 | int -SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2 | int -SQL_GETDATA_EXTENSIONS | int -SQL_GROUP_BY | int -SQL_IDENTIFIER_CASE | int -SQL_IDENTIFIER_QUOTE_CHAR | str -SQL_INDEX_KEYWORDS | int -SQL_INFO_SCHEMA_VIEWS | int -SQL_INSERT_STATEMENT | int -SQL_INTEGRITY | bool -SQL_KEYSET_CURSOR_ATTRIBUTES1 | int -SQL_KEYSET_CURSOR_ATTRIBUTES2 | int -SQL_KEYWORDS | str -SQL_LIKE_ESCAPE_CLAUSE | bool -SQL_MAX_ASYNC_CONCURRENT_STATEMENTS | int -SQL_MAX_BINARY_LITERAL_LEN | int -SQL_MAX_CATALOG_NAME_LEN | int -SQL_MAX_CHAR_LITERAL_LEN | int -SQL_MAX_COLUMNS_IN_GROUP_BY | int -SQL_MAX_COLUMNS_IN_INDEX | int -SQL_MAX_COLUMNS_IN_ORDER_BY | int -SQL_MAX_COLUMNS_IN_SELECT | int -SQL_MAX_COLUMNS_IN_TABLE | int -SQL_MAX_COLUMN_NAME_LEN | int -SQL_MAX_CONCURRENT_ACTIVITIES | int -SQL_MAX_CURSOR_NAME_LEN | int -SQL_MAX_DRIVER_CONNECTIONS | int -SQL_MAX_IDENTIFIER_LEN | int -SQL_MAX_INDEX_SIZE | int -SQL_MAX_PROCEDURE_NAME_LEN | int -SQL_MAX_ROW_SIZE | int -SQL_MAX_ROW_SIZE_INCLUDES_LONG | bool -SQL_MAX_SCHEMA_NAME_LEN | int -SQL_MAX_STATEMENT_LEN | int -SQL_MAX_TABLES_IN_SELECT | int -SQL_MAX_TABLE_NAME_LEN | int -SQL_MAX_USER_NAME_LEN | int -SQL_MULTIPLE_ACTIVE_TXN | bool -SQL_MULT_RESULT_SETS | bool -SQL_NEED_LONG_DATA_LEN | bool -SQL_NON_NULLABLE_COLUMNS | int -SQL_NULL_COLLATION | int -SQL_NUMERIC_FUNCTIONS | int -SQL_ODBC_INTERFACE_CONFORMANCE | int -SQL_ODBC_VER | str -SQL_OJ_CAPABILITIES | int -SQL_ORDER_BY_COLUMNS_IN_SELECT | bool -SQL_PARAM_ARRAY_ROW_COUNTS | int -SQL_PARAM_ARRAY_SELECTS | int -SQL_PROCEDURES | bool -SQL_PROCEDURE_TERM | str -SQL_QUOTED_IDENTIFIER_CASE | int -SQL_ROW_UPDATES | bool -SQL_SCHEMA_TERM | str -SQL_SCHEMA_USAGE | int -SQL_SCROLL_OPTIONS | int -SQL_SEARCH_PATTERN_ESCAPE | str -SQL_SERVER_NAME | str -SQL_SPECIAL_CHARACTERS | str -SQL_SQL92_DATETIME_FUNCTIONS | int -SQL_SQL92_FOREIGN_KEY_DELETE_RULE | int -SQL_SQL92_FOREIGN_KEY_UPDATE_RULE | int -SQL_SQL92_GRANT | int -SQL_SQL92_NUMERIC_VALUE_FUNCTIONS | int -SQL_SQL92_PREDICATES | int -SQL_SQL92_RELATIONAL_JOIN_OPERATORS | int -SQL_SQL92_REVOKE | int -SQL_SQL92_ROW_VALUE_CONSTRUCTOR | int -SQL_SQL92_STR_FUNCTIONS | int -SQL_SQL92_VALUE_EXPRESSIONS | int -SQL_SQL_CONFORMANCE | int -SQL_STANDARD_CLI_CONFORMANCE | int -SQL_STATIC_CURSOR_ATTRIBUTES1 | int -SQL_STATIC_CURSOR_ATTRIBUTES2 | int -SQL_STR_FUNCTIONS | int -SQL_SUBQUERIES | int -SQL_SYSTEM_FUNCTIONS | int -SQL_TABLE_TERM | str -SQL_TIMEDATE_ADD_INTERVALS | int -SQL_TIMEDATE_DIFF_INTERVALS | int -SQL_TIMEDATE_FUNCTIONS | int -SQL_TXN_CAPABLE | int -SQL_TXN_ISOLATION_OPTION | int -SQL_UNION | int -SQL_USER_NAME | str -SQL_XOPEN_CLI_YEAR | str - -## execute - - execute(sql, *params) --> Cursor - -This is just a convenience function for creating a new Cursor, executing the SQL using the -cursor, then discarding the cursor. Since a new Cursor is allocated by each call, this should -not be used if more than one SQL statement needs to be executed. - -## setencoding - - # Python 2 - cnxn.setencoding(type, encoding=None, ctype=None) - - # Python 3 - cnxn.setencoding(encoding=None, ctype=None) - -Sets the text encoding for SQL statements and text parameters. - -### type - -The text type to configure. In Python 2 there are two text types: `str` and `unicode` which -can be configured indivually. Python 3 only has `str` (which is Unicode), so the parameter is -not needed. - - -### encoding - -The encoding to use. This must be a valid Python encoding that converts text to `bytes` -(Python 3) or `str` (Python 2). - -### ctype - -The C data type to use when passing data: `pyodbc.SQL_CHAR` or `pyodbc.SQL_WCHAR`. - -If not provided, `SQL_WCHAR` is used for "utf-16", "utf-16le", and "utf-16be". `SQL_CHAR` is -used for all other encodings. - -The defaults are: - -Python version | type | encoding | ctype --------------- | ---- | -------- | ----- -Python 2 | str | utf-8 | SQL_CHAR -Python 2 | unicode | utf-16le | SQL_WCHAR -Python 3 | unicode | utf-16le | SQL_WCHAR - -If your database driver communicates with only UTF-8 (often MySQL and PostgreSQL), try the -following: - - # Python 2 - cnxn.setencoding(str, encoding='utf-8') - cnxn.setencoding(unicode, encoding='utf-8') - - # Python 3 - cnxn.setencoding(encoding='utf-8') - -In Python 2.7, the value "raw" can be used as special encoding for `str` objects. This will -pass the string object's bytes as-is to the database. This is not recommended as you need to -make sure that the internal format matches what the database expects. - -## setdecoding - - # Python 2 - cnxn.setdecoding(sqltype, encoding=None, ctype=None, to=None) - - # Python 3 - cnxn.setdecoding(sqltype, encoding=None, ctype=None) - -Sets the text decoding used when reading `SQL_CHAR` and `SQL_WCHAR` from the database. - -### sqltype - -The SQL type being configured: `pyodbc.SQL_CHAR` or `pyodbc.SQL_WCHAR`. - -### encoding - -The Python encoding to use when decoding the data. - -### ctype - -The C data type to request from SQLGetData: `pyodbc.SQL_CHAR` or `pyodbc.SQL_WCHAR`. - -### to - -The Python 2 text data type to be returned: `str` or `unicode`. If not provided (recommended), -whatever type the codec returns is returned. (This parameter is not needed in Python 3 because -the only text data type is `str`.) - -The defaults are: - -Python version | type | encoding | ctype --------------- | ---- | -------- | ----- -Python 2 | str | utf-8 | SQL_CHAR -Python 2 | unicode | utf-16le | SQL_WCHAR -Python 3 | unicode | utf-16le | SQL_WCHAR - -In Python 2.7, the value "raw" can be used as special encoding for `SQL_CHAR` values. This -will create a `str` object directly from the bytes from the database with no conversion.string -object's bytes as-is to the database. This is not recommended as you need to make sure that -the internal format matches what the database sends. - -## add_output_converter - - add_output_converter(sqltype, func) - -Register an output converter function that will be called whenever a value with -the given SQL type is read from the database. - -### sqltype - -The integer SQL type value to convert, which can be one of the defined standard constants -(e.g. pyodbc.SQL_VARCHAR) or a database-specific value (e.g. -151 for the SQL Server 2008 -geometry data type). - -### func - -The converter function which will be called with a single parameter, the value, and should -return the converted value. If the value is NULL, the parameter will be None. Otherwise it -will be a Python string. - -## clear_output_converters - - clear_output_converters() - -Remove all output converter functions added by `add_output_converter`. - -## set_attr - - set_attr(attr_id, value) - -Calls SQLSetConnectAttr with the given values. - -### attr_id - -The attribute ID (integer) to set. These are ODBC or driver constants. The ODBC constants can -usually be found in the sql.h or sqlext.h file. - -### value - -The connection attribute value to set. At this time only integer values are supported. diff --git a/docs/api-cursor.md b/docs/api-cursor.md deleted file mode 100644 index b140cf66..00000000 --- a/docs/api-cursor.md +++ /dev/null @@ -1,431 +0,0 @@ - -# Cursor - -Cursors represent a database cursor (and map to ODBC HSTMTs), which is used to manage the -context of a fetch operation. Cursors created from the same connection are not isolated, i.e., -any changes made to the database by a cursor are immediately visible by the other cursors. - -## variables - -### description - -This read-only attribute is a list of 7-item tuples, each containing `(name, type_code, -display_size, internal_size, precision, scale, null_ok)`. pyodbc only provides values for -name, type_code, internal_size, and null_ok. The other values are set to None. - -This attribute will be None for operations that do not return rows or if one of the execute -methods has not been called. - -The type_code member is the class type used to create the Python objects when reading rows. -For example, a varchar column's type will be `str`. - -### rowcount - -The number of rows modified by the previous DDL statement. - -This is -1 if no SQL has been executed or if the number of rows is unknown. Note that it is -not uncommon for databases to report -1 after a select statement for performance reasons. (The -exact number may not be known before the first records are returned to the application.) - -## execute - - cursor.execute(sql, *parameters) --> Cursor - -### sql - -The SQL statement to execute with optional `?` parameter markers. Note that pyodbc *never* -modifies the SQL statement. - -### parameters - -Optional parameters for the markers in the SQL. They can be passed in a single sequence as -defined by the DB API. For convenience, however, they can also be passed individually. - - # as a sequence - cursor.execute("select a from tbl where b=? and c=?", (x, y)) - - # passed individually - cursor.execute("select a from tbl where b=? and c=?", x, y) - -You should use parameters when possible instead of inserting them directly into the SQL to -protect against [SQL injection attacks](http://en.wikipedia.org/wiki/SQL_injection). -Parameters are passed to the database *separately* using an ODBC parameter binding specifically -designed for this. - -The return value is always the cursor itself which allows chaining. - - for row in cursor.execute("select user_id, user_name from users"): - print()row.user_id, row.user_name) - - row = cursor.execute("select * from tmp").fetchone() - rows = cursor.execute("select * from tmp").fetchall() - val = cursor.execute("select count(*) from tmp").fetchval() - - count = cursor.execute("update users set last_logon=? where user_id=?", now, user_id).rowcount - count = cursor.execute("delete from users where user_id=1").rowcount - -The last prepared statement is kept and reused if you execute the same SQL again, making -executing the same SQL with different parameters will be more efficient. - -## executemany - - cursor.executemany(sql, seq_of_parameters) --> Cursor - -Executes the same SQL statement for each set of parameters. - -### seq_of_parameters - -a sequence of sequences - - params = [ ('A', 1), ('B', 2) ] - executemany("insert into t(name, id) values (?, ?)", params) - -This will execute the SQL statement twice, once with `('A', 1)` and once with `('B', 2)`. - -## fetchval - - cursor.fetchval() --> value or None - -Returns the first column value from the next row or None if no more rows are available: - - cursor.execute("select count(*) from users") - c = cursor.fetchval() - -## fetchone - - cursor.fetchone() --> Row or None - -Returns the next Row or `None` if no more data is available. - -A ProgrammingError exception is raised if no SQL has been executed or if it did not return a -result set (e.g. was not a SELECT statement). - - cursor.execute("select user_name from users where user_id=?", userid) - row = cursor.fetchone() - if row: - print(row.user_name) - -## fetchall - - cursor.fetchall() --> list of rows - -Returns a list of all remaining Rows. If there are no more rows, an empty list is returned. - -Since this reads all rows into memory, it should not be used if there are a lot of rows. -Consider iterating over the rows instead. However, it is useful for freeing up a Cursor so you -can perform a second query before processing the resulting rows. - -A ProgrammingError exception is raised if no SQL has been executed or if it did not return a -result set (e.g. was not a SELECT statement). - - cursor.execute("select user_id, user_name from users where user_id < 100") - rows = cursor.fetchall() - for row in rows: - print(row.user_id, row.user_name) - -## fetchmany - - cursor.fetchmany([size=cursor.arraysize]) --> list of rows - -Similar to fetchall but limits the returned list size to `size`. The default for -cursor.arraysize is 1 which is no different than calling fetchone. - -A ProgrammingError exception is raised if no SQL has been executed or if it did not return a -result set (e.g. was not a SELECT statement). - -## commit - - cursor.commit() - -A convenience function that calls commit on the Connection that created this cursor. Since -`cursor` and `commit` are usually the only two functions called on a connection, this function -often allows you to work with cursors only. - -**Warning:** This affects all cursors created by the same connection. - -## rollback - - cursor.rollback() - -A convenience function that calls commit on the Connection that created this cursor. - -**Warning:** This affects all cursors created by the same connection. - -## skip - - cursor.skip(count) - -Skips the next `count` records by calling SQLFetchScroll with SQL_FETCH_NEXT. - -For convenience, `skip(0)` is accepted and will do nothing. - -## nextset - - cursor.nextset() --> True or None - -This method will make the cursor skip to the next available set, discarding any remaining rows -from the current set. - -If there are no more sets, the method returns `None`. Otherwise, it returns a true value and -subsequent calls to the fetch methods will return rows from the next result set. - -This method is useful if you have stored procedures that return multiple results. If your -database supports it, you may be able to send multiple SQL statements in a single batch and use -this method to move through each result set. - - cursor.execute("select * from users; select * from albumts") - users = cursor.fetchall() - cursor.nextset() - albums = cursor.fetchall() - -## close - - cursor.close() - -Closes the cursor. A ProgrammingError exception will be raised if any -operation is attempted with the cursor. - -Cursors are closed automatically when they are deleted, so calling this is -not usually necessary when using C Python. - -## callproc - - cursor.callproc(procname[,parameters]) - -This is not yet supported since there is no way for pyodbc to determine which -parameters are input, output, or both. - -You will need to call stored procedures using execute(). You can use your -database's format or the ODBC escape format. - -## tables - - cursor.tables(table=None, catalog=None, schema=None, tableType=None) --> Cursor - -Creates a result set of tables in the database that match the given criteria. - -Each row has the following columns. See the -[SQLTables](http://msdn.microsoft.com/en-us/library/ms711831.aspx) documentation for more -information. - -column | notes ------- | ----- -table_cat | The catalog name. -table_schem | The schema name. -table_name | The table name. -table_type | One of TABLE, VIEW, SYSTEM TABLE, GLOBAL TEMPORARY, LOCAL TEMPORARY, ALIAS, SYNONYM, or a data source-specific type name. -remarks | A description of the table. - - for row in cursor.tables(): - print(row.table_name) - - # Does table 'x' exist? - if cursor.tables(table='x').fetchone(): - print('yes it does') - -The table, catalog, and schema interpret the '_' and '%' characters as wildcards. The escape -character is driver specific, so use Connection.searchescape. - -## columns - - cursor.columns(table=None, catalog=None, schema=None, column=None) --> Cursor - -Creates a result set of column information in the specified tables using -the [SQLColumns](http://msdn.microsoft.com/en-us/library/ms711683%28VS.85%29.aspx) function. - -column | notes ------- | ----- -table_cat | -table_schem | -table_name | -column_name | -data_type | -type_name | -column_size | -buffer_length | -decimal_digits | -num_prec_radix | -nullable | -remarks | -column_def | -sql_data_type | -sql_datetime_sub | -char_octet_length | -ordinal_position | -is_nullable | One of the pyodbc constants SQL_NULLABLE, SQL_NO_NULLS, SQL_NULLS_UNKNOWN. - - # columns in table x - for row in cursor.columns(table='x'): - print(row.column_name, 'is nullable?', row.is_nullable == pyodbc.SQL_NULLABLE) - -## statistics - - cursor.statistics(table, catalog=None, schema=None, unique=False, quick=True) --> Cursor - -Creates a result set of statistics about a single table and the indexes associated with the -table by -executing [SQLStatistics](http://msdn.microsoft.com/en-us/library/ms711022%28VS.85%29.aspx). - -### unique - -If True only unique indexes are returned. If False all indexes are returned. - -### quick - -If True values for CARDINALITY and PAGES are returned only if they are readily available. -Otherwise `None` is returned for those columns. - -Each row has the following columns: - -| column -| ------ -| table_cat -| table_schem -| table_name -| non_unique -| index_qualifier -| index_name -| type -| ordinal_position -| column_name -| asc_or_desc -| cardinality -| pages -| filter_condition - -## rowIdColumns - - cursor.rowIdColumns(table, catalog=None, schema=None, nullable=True) --> Cursor - -Executes [SQLSpecialColumns](http://msdn.microsoft.com/en-us/library/ms714602%28VS.85%29.aspx) -with `SQL_BEST_ROWID` which creates a result set of columns that uniquely identify a row. - -Each row has the following columns. - -column | notes ------- | ----- -scope | One of SQL_SCOPE_CURROW, SQL_SCOPE_TRANSACTION, or SQL_SCOPE_SESSION -column_name | -data_type | The ODBC SQL data type constant (e.g. SQL_CHAR) -type_name | -column_size | -buffer_length | -decimal_digits | -pseudo_column | One of SQL_PC_UNKNOWN, SQL_PC_NOT_PSEUDO, SQL_PC_PSEUDO - -## rowVerColumns - - cursor.rowVerColumns(table, catalog=None, schema=None, nullable=True) --> Cursor - -Executes [SQLSpecialColumns](http://msdn.microsoft.com/en-us/library/ms714602%28VS.85%29.aspx) -with `SQL_ROWVER` which creates a result set of columns that are automatically updated when any -value in the row is updated. Each row has the following columns. - -keyword | converts to -------- | ----------- -scope | One of SQL_SCOPE_CURROW, SQL_SCOPE_TRANSACTION, or SQL_SCOPE_SESSION -column_name | -data_type | The ODBC SQL data type constant (e.g. SQL_CHAR) -type_name | -column_size | -buffer_length | -decimal_digits | -pseudo_column | One of SQL_PC_UNKNOWN, SQL_PC_NOT_PSEUDO, SQL_PC_PSEUDO - -## primaryKeys - - cursor.primaryKeys(table, catalog=None, schema=None) --> Cursor - -Creates a result set of column names that make up the primary key for a table by executing -the [SQLPrimaryKeys](http://msdn.microsoft.com/en-us/library/ms711005%28VS.85%29.aspx) -function. - -Each row has the following columns: - -| column -| ------ -| table_cat -| table_schem -| table_name -| column_name -| key_seq -| pk_name - -## foreignKeys - - cursor.foreignKeys(table=None, catalog=None, schema=None, foreignTable=None, - foreignCatalog=None, foreignSchema=None) --> Cursor - -Executes the [SQLForeignKeys](http://msdn.microsoft.com/en-us/library/ms709315%28VS.85%29.aspx) -function and creates a result set of column names that are foreign keys in the specified table -(columns in the specified table that refer to primary keys in other tables) or foreign keys in -other tables that refer to the primary key in the specified table. - -Each row has the following columns: - -| column -| ------ -| pktable_cat -| pktable_schem -| pktable_name -| pkcolumn_name -| fktable_cat -| fktable_schem -| fktable_name -| fkcolumn_name -| key_seq -| update_rule -| delete_rule -| fk_name -| pk_name -| deferrability - - -## procedures - - cursor.procedures(procedure=None, catalog=None, schema=None) --> Cursor - -Executes SQLProcedures -and creates a result set of information about the procedures in the data source. Each row has -the following columns: - -| column -| ------ -| procedure_cat -| procedure_schem -| procedure_name -| num_input_params -| num_output_params -| num_result_sets -| remarks -| procedure_type - -## getTypeInfo - - cursor.getTypeInfo(sqlType=None) --> Cursor - -Executes [SQLGetTypeInfo](http://msdn.microsoft.com/en-us/library/ms714632%28VS.85%29.aspx) a -creates a result set with information about the specified data type or all data types supported -by the ODBC driver if not specified. Each row has the following columns: - -| column -| -| type_name -| data_type -| column_size -| literal_prefix -| literal_suffix -| create_params -| nullable -| case_sensitive -| searchable -| unsigned_attribute -| fixed_prec_scale -| auto_unique_value -| local_type_name -| minimum_scale -| maximum_scale -| sql_data_type -| sql_datetime_sub -| num_prec_radix -| interval_precision diff --git a/docs/api-errors.md b/docs/api-errors.md deleted file mode 100644 index 014288fd..00000000 --- a/docs/api-errors.md +++ /dev/null @@ -1,25 +0,0 @@ - -# Errors - -Exceptions are raised when ODBC errors are detected. The exception classes specified in -the [DB API specification](https://www.python.org/dev/peps/pep-0249) are used. - -* DatabaseError - * DataError - * OperationalError - * IntegrityError - * InternalError - * ProgrammingError - -When an error occurs, the type of exception raised is based on the SQLSTATE: - -SQLSTATE | Class --------- | ----- -0A000 | NotSupportedError -22xxx | DataError -23xxx 40002 | IntegrityError -24xxx 25xxx 42xxx | ProgrammingError -All Others | DatabaseError - -A primary key error (attempting to insert a value when the key already exists) will raise an -IntegrityError. diff --git a/docs/api-module.md b/docs/api-module.md deleted file mode 100644 index c76de5f8..00000000 --- a/docs/api-module.md +++ /dev/null @@ -1,100 +0,0 @@ -# pyodbc Module - -## Variables - -### version - -The module version string in *major.minor.patch* format such as `"4.0.2"`. - -### apilevel - -The string constant `"2.0"` indicating this module supports the DB API level 2.0 - -### lowercase - -A Boolean that controls whether column names in result rows are lowercased. This can be changed -any time and affects queries executed after the change. The default is `False`. This can be -useful when database columns have inconsistent capitalization. - -### pooling - -A Boolean indicating whether connection pooling is enabled. This is a global (HENV) setting, so -it can only be modified before the first connection is made. The default is `True`, which -enables ODBC connection pooling. - -### threadsafety - -The value `1`, indicating that threads may share the module but not connections. Note that -connections and cursors may be used by different threads, just not at the same time. - -### paramstyle - -The string constant "qmark" to indicate parameters are identified using question marks. - -## connect - - connect(*connectionstring, **kwargs) --> Connection - -Creates and returns a new connection to the database. - -The connection string and keywords are put together to construct an ODBC connection string. -Python 2 accepts both ANSI and Unicode strings. (In Python 3 all strings are Unicode.) - - # a string - cnxn = connect('driver={SQL Server};server=localhost;database=test;uid=me;pwd=me2') - # keywords - cnxn = connect(driver='{SQL Server}', server='localhost', database='test', uid='me', pwd='me2') - # both - cnxn = connect('driver={SQL Server};server=localhost;database=test', uid='me', pwd='me2') - -The DB API recommends some keywords that are not usually used in ODBC connection strings, so -they are converted: - -keyword | converts to -----------|------------ -host | server -user | uid -password | pwd - - -Some keywords are reserved by pyodbc and are not passed to the odbc driver: - -keyword | notes | default -------- | ----- | ------- -ansi | If True, the driver does not support Unicode and SQLDriverConnectA should be used. | False -attrs_before | A dictionary of connection attributes to set before connecting. | -autocommit | If False, Connection.commit must be called; otherwise each statement is automatically commited | False -encoding | Optional encoding for the connection string. | utf-16le -readonly | If True, the connection is set to readonly | False -timeout | A timeout for the connection, in seconds. This causes the connection's SQL_ATTR_LOGIN_TIMEOUT to be set before the connection occurs. | - -### ansi - -The ansi keyword should only be used to work around driver bugs. pyodbc will determine if the -Unicode connection function (SQLDriverConnectW) exists and always attempt to call it. If the -driver returns IM001 indicating it does not support the Unicode version, the ANSI version is -tried (SQLDriverConnectA). Any other SQLSTATE is turned into an exception. Setting ansi to true -skips the Unicode attempt and only connects using the ANSI version. This is useful for drivers -that return the wrong SQLSTATE (or if pyodbc is out of date and should support other -SQLSTATEs). - -### attrs_before - -The `attrs_before` keyword is an optional dictionary of connection attributes. These will be -set on the connection via SQLSetConnectAttr before a connection is made. - -The dictionary keys must be the integer constant defined by ODBC or the driver. Only integer -values are supported at this time. Below is an example that sets the SQL_ATTR_PACKET_SIZE -connection attribute to 32K. - - SQL_ATTR_PACKET_SIZE = 112 - cnxn = connect(cstring, attrs_before={ SQL_ATTR_PACKET_SIZE : 1024 * 32 }) - -## dataSources - - dataSources() -> { DSN : Description } - -Returns a dictionary mapping available DSNs to their descriptions. - -Note: unixODBC may have a bug that only returns items from the users odbc.ini file without -merging the system one. diff --git a/docs/api-row.md b/docs/api-row.md deleted file mode 100644 index a3cd6d8e..00000000 --- a/docs/api-row.md +++ /dev/null @@ -1,60 +0,0 @@ -# Row - -Row objects are returned from Cursor fetch functions. As specified in the DB API, they are -tuple-like. - - row = cursor.fetchone() - print(row[0]) - -However, there are some pyodbc additions that make them very convenient: - -* Values can be accessed by column name. -* The Cursor.description values can be accessed from the row. -* Values can be replaced -* Rows from the same select share memory. - -Accessing rows by column name is very convenient, readable, and Pythonish: - - cursor.execute("select album_id, photo_id from photos where user_id=1") - row = cursor.fetchone() - print(row.album_id, row.photo_id) - print(row[0], row[1]) # same as above, but less readable - -Having access to the cursor's description, even after the curosr is closed, makes Rows very -convenient data structures -- you can pass them around and they are self describing: - - def getuser(userid): - cnxn = pyodbc.connect(...) - cursor = cnxn.cursor() - return cursor.execute( - """ - select album_id, photo_id - from photos - where user_id = ? - """, userid).fetchall() - - row = getuser(7) - # At this point the cursor has been closed and deleted - # But the columns and datatypes can still be accessed: - print('columns:', ', '.join(t[0] for t in row.cursor_description)) - -Unlike normal tuples, values in Row objects can be replaced. (This means you shouldn't use -rows as dictionary keys!) - -The intention is to make Rows convenient data structures to replace small or short-lived -classes. While SQL is powerful, there are sometimes slight changes that need to be made after -reading values: - - # Replace the 'start_date' datetime in each row with one that has a time zone. - rows = cursor.fetchall() - for row in rows: - row.start_date = row.start_date.astimezone(tz) - -Note that slicing rows returns tuples, not Row objects! - -## Variables - -### cursor_description - -A copy of the Cursor.description object from the Cursor that created this row. This contains -the column names and data types of the columns. diff --git a/docs/encodings.py b/docs/encodings.py deleted file mode 100755 index 81ee2a54..00000000 --- a/docs/encodings.py +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -#- - -import binascii - -chars = ['A', 'å'] -encodings = ['utf-8', 'latin1', 'utf-16-le', 'utf-16-be'] - -for ch in chars: - for enc in encodings: - b = ch.encode(enc) - print(ch, 'in', enc, '-->', binascii.hexlify(b)) diff --git a/docs/index.md b/docs/index.md index ab86cc4b..bdbfadee 100644 --- a/docs/index.md +++ b/docs/index.md @@ -4,245 +4,14 @@ pyodbc is an open source Python module that makes accessing ODBC databases simpl implements the [DB API 2.0](https://www.python.org/dev/peps/pep-0249) specification but is packed with even more Pythonic convenience. -* Getting Started -* API - * [pyodbc](api-module.md) - * [Connection](api-connection.md) - * [Cursor](api-cursor.md) - * [Row](api-row.md) - * [Errors](api-errors.md) -* [Handling Unicode](unicode.md) -* [Releases](releases.md) - -## Installing - -The easiest way to install is using pip. Windows and macOS binaries can often be downloaded by -pip but other operating systems will need to compile from source. +The easiest way to install is to use pip: pip install pyodbc + +Precompiled binary wheels are provided for most Python versions on Windows and macOS. On other +operating systems this will build from source. -If pip needs to compile from source, you'll need to make sure you have the necessary build -packages installed on your system such as "python-devel", etc. See -this -[Building From Source](https://github.com/mkleehammer/pyodbc/wiki/Building-pyodbc-from-source) -wiki page for help. - -Development is on [GitHub](https://github.com/mkleehammer/pyodbc). - -## Getting Started - -### Connect to A Database - -Pass an [ODBC connection string](https://github.com/mkleehammer/pyodbc/wiki) to -the [connect](api-module.md) function which will return a [Connection](api-connection.md). -Once you have a connection you can ask it for a [Cursor](api-cursor.md). Several connection -examples are provided below. - - # Connection example: Windows, without a DSN, using the Windows SQL Server driver - cnxn = pyodbc.connect('DRIVER={SQL Server};SERVER=localhost;PORT=1433;DATABASE=testdb;UID=me;PWD=pass') - - # Connection example: Linux, without a DSN, using the FreeTDS driver - cnxn = pyodbc.connect('DRIVER={FreeTDS};SERVER=localhost;PORT=1433;DATABASE=testdb;UID=me;PWD=pass;TDS_Version=7.0') - - # Connection example: with a DSN - cnxn = pyodbc.connect('DSN=test;PWD=password') - - # Opening a cursor - cursor = cnxn.cursor() - -There is no standard so the keywords depend on the driver you are using. - -### Configure character set encodings - -The default Unicode encoding is UTF-16LE. See the [Unicode section](unicode.md) for tips on -configuring MySQL and PostgreSQL. - - # Python 2.7 - cnxn.setdecoding(pyodbc.SQL_WCHAR, encoding='utf-8') - cnxn.setencoding(str, encoding='utf-8') - cnxn.setencoding(unicode, encoding='utf-8', ctype=pyodbc.SQL_CHAR) - - # Python 3.x - cnxn.setdecoding(pyodbc.SQL_WCHAR, encoding='utf-8') - cnxn.setencoding(encoding='utf-8') - - -### Select Some Data - -All SQL statements are executed using `Cursor.execute`. If the statement returns rows, such as -a select statement, you can retreive them using the Cursor fetch functions (fetchone, fetchall, -fetchval, and fetchmany). - -`Cursor.fetchone` is used to return a single [Row](api-row.md). - - cursor.execute("select user_id, user_name from users") - row = cursor.fetchone() - if row: - print(row) - -Row objects are similar to tuples, but they also allow access to columns by name: - - cursor.execute("select user_id, user_name from users") - row = cursor.fetchone() - print('name:', row[1]) # access by column index - print('name:', row.user_name) # or access by name - -`fetchone` returns `None` when all rows have been retrieved. - - while 1: - row = cursor.fetchone() - if not row: - break - print('id:', row.user_id) - -`Cursor.fetchall` returns all remaining rows in a list. If there are no rows, an empty list is -returned. (If there are a lot of rows, this will use a lot of memory. Unread rows are stored -by the database driver in a compact format and are often sent in batches from the database -server. Reading in only the rows you need at one time will save a lot of memory.) - - cursor.execute("select user_id, user_name from users") - rows = cursor.fetchall() - for row in rows: - print(row.user_id, row.user_name) - -If you are going to process the rows one at a time, you can use the cursor itself as an -iterator: - - cursor.execute("select user_id, user_name from users") - for row in cursor: - print(row.user_id, row.user_name) - -Since `Cursor.execute` always returns the cursor, you can simplify this even more: - - for row in cursor.execute("select user_id, user_name from users"): - print(row.user_id, row.user_name) - -A lot of SQL statements are not very readable on a single line, which is where Python's triple -quoted strings really shine: - - cursor.execute( - """ - select user_id, user_name - from users - where last_logon < '2001-01-01' - and bill_overdue = 1 - """) - -### Parameters - -ODBC supports query parameters using a question mark as a place holder in the SQL. You provide -the values for the question marks by passing them after the SQL: - - cursor.execute( - """ - select user_id, user_name - from users - where last_logon < ? - and bill_overdue = ? - """, '2001-01-01', 1) - -This is safer than putting the values into the string because the parameters are passed to the -database separately, protecting against -[SQL injection attacks](http://en.wikipedia.org/wiki/SQL_injection). It is also more efficient -if you execute the same SQL repeatedly with different parameters. The SQL will -be [prepared](http://en.wikipedia.org/wiki/Prepared_statements#Parameterized_statements) only -once. (pyodbc only keeps the last statement prepared, so if you switch between statements, -each will be prepared multiple times.) - -The Python DB API specifies that parameters should be passed in a sequence, so this is also -supported by pyodbc: - - cursor.execute( - """ - select user_id, user_name - from users - where last_logon < ? - and bill_overdue = ? - """, ['2001-01-01', 1]) - -### Insert Data - -Inserting data uses the same function - pass the insert SQL to execute along with any parameters. - - cursor.execute("insert into products(id, name) values ('pyodbc', 'awesome library')") - cnxn.commit() - - cursor.execute("insert into products(id, name) values (?, ?)", 'pyodbc', 'awesome library') - cnxn.commit() - -Note the calls to cnxn.commit(). Connections maintain a transaction that is rolled back if -commit is not called. This makes error recovery easy (and finally is not needed). - -**Warning*:* You must call commit or your changes will be lost or you must turn auto-commit on. - -### Update and Delete - -Updating and deleting work the same way: pass the SQL to execute. However, you often want to -know how many records were affected when updating and deleting, in which case you can use -`Cursor.rowcount` value: - - cursor.execute("delete from products where id <> ?", 'pyodbc') - print('Deleted {} inferior products'.format(cursor.rowcount)) - cnxn.commit() - -Since `execute` returns the cursor (allowing you to chain calls or use in an iterator), you -will sometimes see code with rowcount on the end: - - deleted = cursor.execute("delete from products where id <> 'pyodbc'").rowcount - cnxn.commit() - -**Warning*:* You must call commit or your changes will be lost or you must turn auto-commit on. - -## Tips and Tricks - -### Quotes - -Since single quotes are valid in SQL, getting into the habit of using double quotes to surround -your SQL will be convenient: - - cursor.execute("delete from products where id <> 'pyodbc'") - -If you are using triple quotes, you can use either: - - cursor.execute( - """ - delete - from products - where id <> 'pyodbc' - """) - -### Column names - -Some databases like Microsoft SQL Server do not generate column names for calculations, in -which case you need to access the columns by index. You can also use the 'as' keyword to name -columns (the "as user_count" in the SQL below). - - row = cursor.execute("select count(*) as user_count from users").fetchone() - print('{} users'.format(row.user_count) - -Of course you can always extract the value by index or use `fetchval`: - - count = cursor.execute("select count(*) from users").fetchval() - print('{} users'.format(count) - -If there is a default value, often you can is `ISNULL` or `coalesce`: - - maxid = cursor.execute("select coalesce(max(id), 0) from users").fetchval() - -### Automatic Cleanup - -Connections (by default) are always in a transaction. If a connection is closed without being -committed, the current transaction is rolled back. Because connections are closed immediately -when they go out of scope (due to C Python's reference counting implementation) cleanup rarely -requires `except` or `finally` clauses. +The rest of the pyodbc documentation is now on a Wiki so it can be maintained by the entire community. -For example, if either of the execute statements fails in the example below, an exception would -be raised causing both the connection cursor to go out of scope as the exception leaves the -function and unwinds the stack. Either both inserts take place or neither, all with no clean -up code to write. +[pyodbc Documentation](https://github.com/mkleehammer/pyodbc/wiki) - cnxn = pyodbc.connect(...) - cursor = cnxn.cursor() - cursor.execute("insert into t(col) values (1)") - cursor.execute("insert into t(col) values (2)") - cnxn.commit() diff --git a/docs/releases.md b/docs/releases.md deleted file mode 100644 index 557281e8..00000000 --- a/docs/releases.md +++ /dev/null @@ -1,71 +0,0 @@ - -# Releases - -# 4.0.0 / 2016-12-30 - -Unicode handling rewritten for correctness. See the [Handling Unicode](unicode.md) -documentation for details. - -# 3.0.10 / 2015-04-29 - -Binary distributions for OS X and Windows are now uploaded -to [PyPI](https://pypi.python.org/pypi?name=pyodbc) and can now be installed using pip. Source -distributions are also uploaded and can be used by other platforms to compile. (PyPI only -allows binary uploads for OS X and Windows.) - -Moved the project from Google Code hosting to GitHub. (Google is closing its open source -hosting.) - -Fixed potential load failure. - -Fixed decimal and int issues on 64-bit linux. - -# 3.0.7 / 2013-05-19 - -Added context manager support to Cursor - -Added padding for driver bugs writing an extra byte - -Cursor.executemany now accepts an iterator or generator. - -Compilation improvements for FreeBSD, Cygwin, and OS/X - -Use SQL_DATA_AT_EXEC instead of SQL_DATA_LEN_AT_EXEC when possible for driver compatibility. - -Row objects can now be pickled. - -# 3.0.6 / 2012-06-25 - -Added Cursor.commit() and Cursor.rollback(). It is now possible to use only a cursor in your -code instead of keeping track of a connection and a cursor. - -Added readonly keyword to connect. If set to True, SQLSetConnectAttr SQL_ATTR_ACCESS_MODE is -set to SQL_MODE_READ_ONLY. This may provide better locking semantics or speed for some drivers. - -Fixed an error reading SQL Server XML data types longer than 4K. - -# 3.0.5 / 2012-02-23 - -Fixed "function sequence" errors caused by prepared SQL not being cleared ("unprepared") when a -catalog function is executed. - -# 3.0.4 / 2012-01-13 - -Fixed building on Python 2.5. Other versions are not affected. - -# 3.0.3 / 2011-12-28 - -Update to build using gcc 4.6.2: A compiler warning was changed to an error -in 4.6.2. - -# 3.0.2 / 2011-12-26 - -This is the first official pyodbc release that supports both Python 2 and Python 3 (3.2+). -Merry Christmas! - -Many updates were made for this version, particularly around Unicode support. If you have -outstanding issues from the 2.1.x line, please retest with this version. - -If you are on Linux and connecting to SQL Server, you may also be interested -in [Microsoft's Linux ODBC driver](http://www.microsoft.com/download/en/details.aspx?id=28160). -Check your bitness - a Google search may be required ;) diff --git a/docs/unicode.md b/docs/unicode.md deleted file mode 100644 index 8d37fe75..00000000 --- a/docs/unicode.md +++ /dev/null @@ -1,156 +0,0 @@ -# Unicode - -## TL;DR - -By default, pyodbc uses UTF-16LE and SQL_C_WCHAR for reading and writing all Unicode as -recommended in the ODBC specification. Unfortunately many drivers behave differently so -connections may need to be configured. I recommend creating a global connection factory where -you can consolidate your connection string and configuration: - - def connect(): - cnxn = pyodbc.connect(_connection_string) - cnxn.setencoding('utf-8') - return cnxn - -### Configuring Specific Databases - -#### Microsoft SQL Server - -SQL Server's recent drivers match the specification, so no configuration is necessary. Using -the pyodbc defaults is recommended. - -However, if you want Python 2.7 `str` results instead of unicode results you may be able to -improve performance by setting the encoding to match the database's collation. In particular, -it is common to use a latin1 character set and Python has a built-in latin1 codec. - -Check your SQL Server collation using: - - select serverproperty('collation') - -If it is something like "SQL_Latin1_General_CP1_CI_AS" and you want `str` results, you *may* -try: - - cnxn.setdecoding(pyodbc.SQL_CHAR, encoding='latin1', to=str) - cnxn.setencoding(str, encoding='latin1') - -It is not recommended, but you can also set the encoding to "raw" which will pass bytes -directly between Python `str` objects and database SQL_C_CHAR buffers. This should only be -used if you are certain you know what you are doing as it may not be clear when it doesn't -work. It will only work if the database bytes are in the same format is Python's internal -format. This is compatible with pyodbc 3.x. - - cnxn.setdecoding(pyodbc.SQL_CHAR, encoding='raw') - cnxn.setencoding(str, encoding='raw') - - -#### MySQL, PostgreSQL, and Teradata - -These databases tend to use a single encoding and do not differentiate between "SQL_CHAR" and -"SQL_WCHAR". Therefore you must configure them to encode Unicode data as UTF-8 and to decode -both C buffer types using UTF-8. - - # Python 2.7 - cnxn.setdecoding(pyodbc.SQL_CHAR, encoding='utf-8') - cnxn.setdecoding(pyodbc.SQL_WCHAR, encoding='utf-8') - cnxn.setencoding(str, encoding='utf-8') - cnxn.setencoding(unicode, encoding='utf-8') - - # Python 3.x - cnxn.setdecoding(pyodbc.SQL_CHAR, encoding='utf-8') - cnxn.setdecoding(pyodbc.SQL_WCHAR, encoding='utf-8') - cnxn.setencoding(encoding='utf-8') - -If you are using MySQL, you can add the character set to the connection string, but I'm not -sure if this is necessary. - - # MySQL - cstring = 'DSN=mydsn;CharSet=utf8' - cnxn = pyodbc.connect(cstring) - - -## Details - -### Encodings - -The Unicode standard specifies numeric "codes" for each character, such as 65 for 'A' and 229 -for 'å' (latin small letter a with ring above). However, it does *not* specify how these -numbers should be represented in a computer's memory. The same characters can be represented -in multiple ways which are called *encodings*. For example, here are the example characters -above in some different encodings: - -| Character | Encoding | Bytes | -| --------- | -------- | ----- | -| A | latin1 | 0x41 | -| A | utf-8 | 0x41 | -| A | utf-16-le | 0x4100 | -| A | utf-16-be | 0x0041 | -| å | latin1 | 0xe5 | -| å | utf-8 | 0xc3a5 | -| å | utf-16-le | 0xe500 | -| å | utf-16-be | 0x00e5 | - -ASCII characters, such as "A", have values less than 128 and are easy to store in a single -byte. Values greater than 127 sometimes are encoded with multiple bytes even if the value -itself could fit in a single byte. Notice the UTF-8 encoding of "å" is 0xc3a5 even though its -value fits in the single latin1 byte 0xe5. - -**IMPORTANT:** The thing to note here is that when converting text to bytes, *some* encoding -must be chosen. Even a string as simple as "A" has more than one binary format, as the table -above makes clear. - -### ODBC Conversions - -The ODBC specification defines two C data types for character data: - -* SQL_CHAR: A single-byte type like the C `char` data type, though the sign may differ. -* SQL_WCHAR: A two-byte data type like a 2-byte `wchar_t` - -Originally ODBC specified Unicode data to be encoded using UCS-2 and transferred in SQL_WCHAR -buffers. Later this was changed to allow UTF-8 in SQL_CHAR buffers and UTF-16LE in SQL_WCHAR -buffers. - -By default pyodbc reads all text columns as SQL_C_WCHAR buffers and decodes them using UTF-16LE. -When writing, it always encodes using UTF-16LE into SQL_C_WCHAR buffers. - -### Configuring - -If the defaults above do not match your database driver, you can use the -`Connection.setencoding` and `Connection.setdecoding` functions. - -#### Python 3 - - cnxn.setencoding(encoding=None, ctype=None) - -This sets the encoding used when writing an `str` object to the database and the C data type. -(The data is always written to an array of bytes, but we must tell the database if it should -treat the buffer as an array of SQL_CHARs or SQL_WCHARs.) - -The `encoding` must be a valid Python encoding that converts text to `bytes`. Optimized code -is used for "utf-8", "utf-16", "utf-16le", and "utf-16be". - -The result is always an array of bytes but we must also tell the ODBC driver if it should treat -the buffer as an array of SQL_CHAR or SQL_WCHAR elements. If not provided, `pyodbc.SQL_WCHAR` -is used for the UTF-16 variants and `pyodbc.SQL_CHAR` is used for everything else. - - cnxn.setdecoding(sqltype, encoding=None, ctype=None) - -This sets the encoding used when reading a buffer from the database and converting it to an -`str` object. - -`sqltype` controls which SQL type being configured: `pyodbc.SQL_CHAR` or `pyodbc.SQL_WCHAR`. -Use SQL_CHAR to configure the encoding when reading a SQL_CHAR buffer, etc. - -When a buffer is being read, the driver will tell pyodbc whether it is a SQL_CHAR or SQL_WCHAR -buffer. However, pyodbc can request the data be converted to either of these formats. Most of -the time this parameter should not be supplied and the same type will be used, but this can be -useful in the case where the driver reports SQL_WCHAR data but the data is actually in UTF-8. - -#### Python 2 - -The Python 2 version of setencoding starts with a type parameter so that `str` and `unicode` -can be configured independantly. - - # cnxn.setencoding(type, encoding=None, ctype=None) - - cnxn.setencoding(str, encoding='utf-8') - cnxn.setencoding(unicode, encoding='utf-8', ctype=pyodbc.SQL_CHAR)