Repository: sopel-irc/sopel

Issue:
db: create table "nicknames" fails with MySQL (VARCHAR requires length) 
Testing the new database system. According to some quick google research as well as some looking at from @Exirel , "The `String` type requires a lenght in most DB".

Config:
````
db_type = mysql
db_user = sopeluser
db_pass = sopelpass
db_host = 127.0.0.1
db_port = 3306
db_name = sopel
````

Traceback:

````
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/sql/compiler.py", line 2845, in visit_create_table
    create_column, first_pk=column.primary_key and not first_pk
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/sql/compiler.py", line 350, in process
    return obj._compiler_dispatch(self, **kwargs)
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/sql/visitors.py", line 91, in _compiler_dispatch
    return meth(self, **kw)
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/sql/compiler.py", line 2877, in visit_create_column
    text = self.get_column_specification(column, first_pk=first_pk)
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/dialects/mysql/base.py", line 1490, in get_column_specification
    column.type, type_expression=column
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/sql/compiler.py", line 400, in process
    return type_._compiler_dispatch(self, **kw)
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/sql/visitors.py", line 91, in _compiler_dispatch
    return meth(self, **kw)
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/sql/compiler.py", line 3342, in visit_string
    return self.visit_VARCHAR(type_, **kw)
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/dialects/mysql/base.py", line 1951, in visit_VARCHAR
    "VARCHAR requires a length on dialect %s" % self.dialect.name
sqlalchemy.exc.CompileError: VARCHAR requires a length on dialect mysql
 
The above exception was the direct cause of the following exception:
 
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/dist-packages/sopel/cli/run.py", line 64, in run
    p = bot.Sopel(settings, daemon=daemon)
  File "/usr/local/lib/python3.6/dist-packages/sopel/bot.py", line 123, in __init__
    self.db = SopelDB(config)
  File "/usr/local/lib/python3.6/dist-packages/sopel/db.py", line 145, in __init__
    BASE.metadata.create_all(self.engine)
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/sql/schema.py", line 4287, in create_all
    ddl.SchemaGenerator, self, checkfirst=checkfirst, tables=tables
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/engine/base.py", line 2033, in _run_visitor
    conn._run_visitor(visitorcallable, element, **kwargs)
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/engine/base.py", line 1607, in _run_visitor
    visitorcallable(self.dialect, self, **kwargs).traverse_single(element)
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/sql/visitors.py", line 131, in traverse_single
    return meth(obj, **kw)
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/sql/ddl.py", line 781, in visit_metadata
    _is_metadata_operation=True,
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/sql/visitors.py", line 131, in traverse_single
    return meth(obj, **kw)
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/sql/ddl.py", line 826, in visit_table
    include_foreign_key_constraints,
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/engine/base.py", line 988, in execute
    return meth(self, multiparams, params)
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/sql/ddl.py", line 72, in _execute_on_connection
    return connection._execute_ddl(self, multiparams, params)
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/engine/base.py", line 1043, in _execute_ddl
    else None,
  File "<string>", line 1, in <lambda>
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/sql/elements.py", line 462, in compile
    return self._compiler(dialect, bind=bind, **kw)
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/sql/ddl.py", line 29, in _compiler
    return dialect.ddl_compiler(dialect, self, **kw)
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/sql/compiler.py", line 319, in __init__
    self.string = self.process(self.statement, **compile_kwargs)
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/sql/compiler.py", line 350, in process
    return obj._compiler_dispatch(self, **kwargs)
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/sql/visitors.py", line 91, in _compiler_dispatch
    return meth(self, **kw)
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/sql/compiler.py", line 2857, in visit_create_table
    % (table.description, column.name, ce.args[0])
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/util/compat.py", line 383, in raise_from_cause
    reraise(type(exception), exception, tb=exc_tb, cause=cause)
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/util/compat.py", line 128, in reraise
    raise value.with_traceback(tb)
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/sql/compiler.py", line 2845, in visit_create_table
    create_column, first_pk=column.primary_key and not first_pk
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/sql/compiler.py", line 350, in process
    return obj._compiler_dispatch(self, **kwargs)
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/sql/visitors.py", line 91, in _compiler_dispatch
    return meth(self, **kw)
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/sql/compiler.py", line 2877, in visit_create_column
    text = self.get_column_specification(column, first_pk=first_pk)
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/dialects/mysql/base.py", line 1490, in get_column_specification
    column.type, type_expression=column
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/sql/compiler.py", line 400, in process
    return type_._compiler_dispatch(self, **kw)
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/sql/visitors.py", line 91, in _compiler_dispatch
    return meth(self, **kw)
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/sql/compiler.py", line 3342, in visit_string
    return self.visit_VARCHAR(type_, **kw)
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/dialects/mysql/base.py", line 1951, in visit_VARCHAR
    "VARCHAR requires a length on dialect %s" % self.dialect.name
sqlalchemy.exc.CompileError: (in table 'nicknames', column 'slug'): VARCHAR requires a length on dialect mysql
````



Discussion:
The important error message is here:

> sqlalchemy.exc.CompileError: (in table 'nicknames', column 'slug'): VARCHAR requires a length on dialect mysql

And the [documentation for String type](https://docs.sqlalchemy.org/en/13/core/type_basics.html#sqlalchemy.types.String) for SQLAlchemy:

> length – optional, a length for the column for use in DDL and CAST expressions. May be safely omitted if no CREATE TABLE will be issued. Certain databases may require a length for use in DDL, and will raise an exception when the CREATE TABLE DDL is issued if a VARCHAR with no length is included. Whether the value is interpreted as bytes or characters is database specific.
Mobile fat-fingered the assignments, blah.

@RustyBower you want to take this one?
That said, it's probably as simple as adding `(255)` to both of these `String`s:

https://github.com/sopel-irc/sopel/blob/59f446c70c149959bb54e315c7b96f193645791e/sopel/db.py#L53-L54

Resolving patch:
diff --git a/sopel/db.py b/sopel/db.py
--- a/sopel/db.py
+++ b/sopel/db.py
@@ -50,8 +50,8 @@ class Nicknames(BASE):
     """
     __tablename__ = 'nicknames'
     nick_id = Column(Integer, ForeignKey('nick_ids.nick_id'), primary_key=True)
-    slug = Column(String, primary_key=True)
-    canonical = Column(String)
+    slug = Column(String(255), primary_key=True)
+    canonical = Column(String(255))
 
 
 class NickValues(BASE):


Test patch: